How Clients Authenticate with Ed25519 or P‑256 Signatures in mpCium
mpCium implements a dual-signature authentication scheme allowing clients to prove identity using either Ed25519 (EdDSA) or P‑256 (ECDSA) keys, with the LocalSigner type in pkg/client/local_signer.go dispatching to algorithm-specific signing routines and the server verifying via pkg/identity/identity.go.
The fystack/mpcium repository provides a cryptographic authentication layer that supports both modern Ed25519 curves and NIST P‑256 ECDSA signatures. This flexibility enables integration with diverse blockchain ecosystems while maintaining a unified verification interface in the identity package.
The Three-Stage Authentication Flow
mpCium authenticates clients through a structured three-stage process involving key initialization, payload signing, and server-side verification according to the specific cryptographic algorithm configured.
Stage 1: Key Selection and Loading
The client creates a LocalSigner that determines which algorithm to use based on the EventInitiatorKeyTypeEd25519 or EventInitiatorKeyTypeP256 constant. In pkg/client/local_signer.go, the NewLocalSigner constructor (lines 33‑66) reads the key material from a file path specified in LocalSignerOptions, automatically detecting .age extensions for encrypted keys.
The constructor dispatches to algorithm-specific loaders:
loadEd25519Key(lines 99‑106) parses the 32‑byte private key for Ed25519.loadP256Key(lines 109‑116) loads the private key for P‑256 curve operations.
Stage 2: Message Signing
The client constructs an initiator message such as SignTxMessage or GenerateKeyMessage defined in pkg/types/initiator_msg.go. Before transmission, the message must include a signature over its canonical payload.
The LocalSigner.Sign method (lines 22‑34 of local_signer.go) automatically dispatches to the appropriate cryptographic routine:
- Ed25519: calls
ed25519.Signfrom the Go standard library. - P‑256: calls
encryption.SignWithP256frompkg/encryption/p256.go.
The signer invokes msg.Raw() (implemented at lines 64‑80 of initiator_msg.go) to obtain a deterministic JSON payload excluding the Signature field itself, then stores the resulting signature in the message's Signature field.
Stage 3: Server-Side Verification
When the mpCium node receives the initiator message, pkg/identity/identity.go handles verification using public keys stored in the node's identity file and optional authorizer configurations.
For the initiator signature, the system selects the verifier based on the configured key type:
fileStore.verifyEd25519(lines 55‑66) retrieves the stored Ed25519 public key viaGetPublicKey(lines 39‑49) and executesed25519.Verify.fileStore.verifyP256(lines 71‑82) callsencryption.VerifyP256Signatureto perform ECDSA verification on the P‑256 curve.
If the message includes authorizer signatures defined in the authorizer_public_keys section of config.yaml, the verifyAuthorizerSignature function (lines 20‑42) matches each authorizer's algorithm field (AlgorithmEd25519 or AlgorithmP256) to the cached key type and invokes the corresponding verification routine.
Implementation Examples
Authenticating with Ed25519
To authenticate using Ed25519, initialize the signer with EventInitiatorKeyTypeEd25519 and construct the transaction message:
// Load the Ed25519 signer from pkg/client/local_signer.go
signer, err := client.NewLocalSigner(
types.EventInitiatorKeyTypeEd25519,
client.LocalSignerOptions{KeyPath: "./event_initiator.key"},
)
if err != nil {
log.Fatal(err)
}
// Build the transaction request
txMsg := &types.SignTxMessage{
KeyType: types.KeyTypeEd25519,
WalletID: "wallet-123",
NetworkInternalCode: "solana-devnet",
TxID: uuid.New().String(),
Tx: []byte{0xde, 0xad, 0xbe, 0xef},
}
// Sign and publish via the MPC client
mpcClient := client.NewMPCClient(client.Options{
NatsConn: natsConn,
Signer: signer,
})
if err := mpcClient.SignTransaction(txMsg); err != nil {
log.Fatal(err)
}
Behind the scenes, SignTransaction internally calls signer.Sign(txMsg.Raw()), which triggers ed25519.Sign from the standard library. The server-side verifyEd25519 function in pkg/identity/identity.go validates the signature against the initiator's stored public key.
Authenticating with P‑256
For P‑256 authentication, specify EventInitiatorKeyTypeP256 during signer creation. Note that wallet key types use KeyTypeSecp256k1 to indicate P‑256 curve usage:
// Load the P‑256 signer
signer256, err := client.NewLocalSigner(
types.EventInitiatorKeyTypeP256,
client.LocalSignerOptions{KeyPath: "./event_initiator_p256.key"},
)
if err != nil {
log.Fatal(err)
}
// Construct the message with secp256k1 key type for P‑256 wallets
msg := &types.SignTxMessage{
KeyType: types.KeyTypeSecp256k1, // Indicates P‑256 wallet
WalletID: "wallet-456",
NetworkInternalCode: "ethereum-mainnet",
TxID: uuid.New().String(),
Tx: []byte("transaction_data"),
}
// The client automatically uses encryption.SignWithP256
mpcClient := client.NewMPCClient(client.Options{
NatsConn: natsConn,
Signer: signer256,
})
mpcClient.SignTransaction(msg)
Verification follows the verifyP256 path in identity.go, which delegates to encryption.VerifyP256Signature using the stored ECDSA public key.
Verifying Authorizer Signatures
Authorizers provide additional signatures configured in config.yaml with explicit algorithm declarations:
authorizer_public_keys:
auditor1:
public_key: "a1b2c3d4..." # 32-byte hex for Ed25519
algorithm: "ed25519"
compliance2:
public_key: "04d5e6f7..." # DER-encoded P‑256 key
algorithm: "p256"
When including authorizer signatures in the message:
rawPayload, _ := txMsg.Raw()
txMsg.AuthorizerSignatures = []types.AuthorizerSignature{
{
AuthorizerID: "auditor1",
Signature: ed25519.Sign(privEd25519, rawPayload),
},
{
AuthorizerID: "compliance2",
Signature: encryption.SignWithP256(privP256, rawPayload),
},
}
The server's verifyAuthorizerSignature function automatically selects ed25519.Verify or encryption.VerifyP256Signature based on the algorithm field in the configuration.
Summary
- Dual-algorithm support: mpCium clients authenticate using either Ed25519 or P‑256 signatures via the
LocalSignerinterface inpkg/client/local_signer.go. - Automatic dispatch: The
Signmethod (lines 22‑34) automatically routes toed25519.Signorencryption.SignWithP256based on the initiator key type specified during construction. - Deterministic payloads: All signatures are computed over the canonical output of
msg.Raw()(lines 64‑80 ofpkg/types/initiator_msg.go), ensuring consistent verification data. - Flexible verification: The server-side
identity.gofile providesverifyEd25519(lines 55‑66) andverifyP256(lines 71‑82) for initiators, plusverifyAuthorizerSignature(lines 20‑42) for multi-party authorization.
Frequently Asked Questions
How do I configure an mpCium client to use Ed25519 instead of P‑256?
Pass types.EventInitiatorKeyTypeEd25519 as the first argument to client.NewLocalSigner in pkg/client/local_signer.go, and ensure your key file contains a valid 32‑byte Ed25519 private key. The constructor (lines 33‑66) automatically selects the Ed25519 loading path via loadEd25519Key (lines 99‑106).
What determines whether the server uses Ed25519 or P‑256 verification?
The server checks the initiator's configured key type stored in the identity file, then dispatches to either fileStore.verifyEd25519 or fileStore.verifyP256 in pkg/identity/identity.go (lines 55‑82). Each function retrieves the appropriate public key via GetPublicKey (lines 39‑49) and calls the corresponding verification routine.
Can authorizers use a different signature algorithm than the initiator?
Yes. The verifyAuthorizerSignature function (lines 20‑42 of pkg/identity/identity.go) reads the algorithm field from the authorizer_public_keys configuration and independently selects Ed25519 or P‑256 verification. This allows mixed-algorithm scenarios where, for example, an initiator uses Ed25519 while authorizers use P‑256.
Where is the P‑256 signing logic implemented?
P‑256 signing is implemented in pkg/encryption/p256.go via the SignWithP256 function, which the LocalSigner calls when configured with EventInitiatorKeyTypeP256. Verification uses encryption.VerifyP256Signature (also in p256.go) invoked by verifyP256 in pkg/identity/identity.go.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →