# How AWS KMS Integration Works for Production Signing in Mpcium

> Learn how Mpcium integrates with AWS KMS for production signing. Securely manage private keys with KMS, enhancing zero-exposure deployments for your applications.

- Repository: [Fystack Labs/mpcium](https://github.com/fystack/mpcium)
- Tags: how-to-guide
- Published: 2026-03-02

---

**Mpcium delegates all signing operations to AWS KMS, keeping private keys inside the service while nodes only handle the P-256 public key, enabling zero-exposure production deployments.**

AWS KMS integration for production signing is the recommended security model for the `fystack/mpcium` MPC wallet infrastructure. By externalizing cryptographic operations to KMS, nodes never possess signing material, eliminating key-exfiltration risks while maintaining full compatibility with the library's threshold signing workflows.

## Why Use AWS KMS for Production Signing?

Production deployments benefit from three core security properties implemented in the `KMSSigner`:

- **Zero private key exposure** – The ECDSA P-256 private key remains inside AWS KMS. Nodes call the `Sign` API and receive only the raw signature bytes, forwarding them to the MPC protocol without ever seeing the key material.
- **IAM role-based authentication** – When running on AWS compute (ECS, EC2, EKS), the AWS SDK automatically assumes the attached IAM role, removing the need for static credentials in configuration files or environment variables.
- **Algorithm enforcement** – The implementation strictly validates that the requested key type is `EventInitiatorKeyTypeP256`, ensuring only ECDSA with P-256 and SHA-256 is used for all operations.

## Core Architecture of the KMSSigner

### The KMSSigner Struct

The `KMSSigner` struct in `pkg/client/kms_signer.go:18-24` encapsulates the AWS SDK client and key metadata:

```go
type KMSSigner struct {
    keyType   types.EventInitiatorKeyType
    client    *kms.Client
    keyID     string
    publicKey []byte // cached hex-encoded public key
}

```

This design caches the public key after initial retrieval to avoid repeated KMS API calls during the node lifecycle.

### Configuration with KMSSignerOptions

Configuration is handled through `KMSSignerOptions` defined at `pkg/client/kms_signer.go:26-33`:

```go
type KMSSignerOptions struct {
    Region          string
    KeyID           string
    EndpointURL     string // optional: for LocalStack
    AccessKeyID     string // optional: static credentials
    SecretAccessKey string // optional: static credentials
}

```

The options struct supports multiple deployment patterns—from production IAM roles to local development with static keys or LocalStack endpoints.

### Initialization and Public Key Loading

The `NewKMSSigner` function at `pkg/client/kms_signer.go:35-90` orchestrates setup:

1. Validates that `keyType` is `EventInitiatorKeyTypeP256`, returning an error for unsupported algorithms.
2. Constructs an AWS SDK configuration using `config.LoadDefaultConfig`, which automatically resolves credentials from the environment, shared config, or IAM role.
3. Creates a `kms.Client` with the loaded configuration.
4. Calls `loadPublicKey` to retrieve and cache the public key.

The `loadPublicKey` method at `pkg/client/kms_signer.go:93-122` invokes `kms:GetPublicKey`, parses the DER-encoded response, validates the ECDSA P-256 curve, and stores the hex-encoded public key for subsequent access via the `PublicKey()` method.

## Implementing AWS KMS Signing in Production

### IAM Role-Based Authentication

For production workloads running on AWS infrastructure, omit static credentials from `KMSSignerOptions`. The AWS SDK's default credential chain automatically assumes the IAM role attached to the EC2 instance, ECS task, or EKS pod. Ensure the role's policy includes:

```json
{
    "Effect": "Allow",
    "Action": [
        "kms:Sign",
        "kms:GetPublicKey"
    ],
    "Resource": "arn:aws:kms:us-east-1:123456789012:key/abcd1234-ef56-7890-abcd-1234567890ab"
}

```

### The Signing Workflow

When the MPC protocol requires a signature, the `Sign` method at `pkg/client/kms_signer.go:124-143` executes:

```go
func (k *KMSSigner) Sign(msg []byte) ([]byte, error) {
    input := &kms.SignInput{
        KeyId:            aws.String(k.keyID),
        Message:          msg,
        MessageType:      types.MessageTypeRaw,
        SigningAlgorithm: types.SigningAlgorithmSpecEcdsaSha256,
    }
    result, err := k.client.Sign(context.TODO(), input)
    if err != nil {
        return nil, fmt.Errorf("KMS sign failed: %w", err)
    }
    return result.Signature, nil
}

```

This implementation sends the raw message to KMS, which returns the DER-encoded ECDSA signature. The signature is then fed into Mpcium's MPC workflow without the node ever handling private key material.

### Retrieving the Public Key for Configuration

After initializing the signer, extract the public key to populate the node's configuration:

```go
pubKeyHex, err := kmsSigner.PublicKey()
if err != nil {
    logger.Fatal("Failed to get public key", err)
}
logger.Info("Configure event_initiator_pubkey", "key", pubKeyHex)

```

This hex-encoded value must be set in [`config.yaml`](https://github.com/fystack/mpcium/blob/main/config.yaml) as `event_initiator_pubkey` so that peer nodes can verify initiator signatures during the MPC protocol.

## Development and Testing Alternatives

### Static Credentials for Local Development

When running outside AWS infrastructure (e.g., local laptops), provide explicit credentials via `KMSSignerOptions`:

```go
kmsSigner, err := client.NewKMSSigner(
    types.EventInitiatorKeyTypeP256,
    client.KMSSignerOptions{
        Region:          "us-east-1",
        KeyID:           "arn:aws:kms:us-east-1:123456789012:key/abcd1234-ef56-7890-abcd-1234567890ab",
        AccessKeyID:     "AKIAIOSFODNN7EXAMPLE",
        SecretAccessKey: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
    },
)

```

*Source: README local dev snippet* – `README.md:60-71`

### LocalStack Integration

For integration testing without AWS access, configure the signer to point at a LocalStack instance:

```go
kmsSigner, err := client.NewKMSSigner(
    types.EventInitiatorKeyTypeP256,
    client.KMSSignerOptions{
        Region:      "us-east-1",
        KeyID:       "alias/my-test-key",
        EndpointURL: "http://localhost:4566",
        AccessKeyID:     "test",
        SecretAccessKey: "test",
    },
)

```

*Source: LocalStack example* – `examples/generate/kms/main.go:48-53`

## Summary

- **Zero-exposure security**: The `KMSSigner` in [`pkg/client/kms_signer.go`](https://github.com/fystack/mpcium/blob/main/pkg/client/kms_signer.go) ensures private keys never leave AWS KMS, with nodes only caching the P-256 public key.
- **Flexible authentication**: Production deployments use IAM roles automatically, while development environments can use static credentials or LocalStack endpoints via `KMSSignerOptions`.
- **Strict algorithm enforcement**: The implementation validates `EventInitiatorKeyTypeP256` at initialization and uses `SigningAlgorithmSpecEcdsaSha256` for all KMS operations.
- **Seamless MPC integration**: After initialization, the signer exposes `PublicKey()` for configuration and `Sign()` for the MPC workflow, handling raw ECDSA signatures via the KMS `Sign` API.

## Frequently Asked Questions

### How does Mpcium ensure the private key never leaves AWS KMS?

The `KMSSigner` implementation in [`pkg/client/kms_signer.go`](https://github.com/fystack/mpcium/blob/main/pkg/client/kms_signer.go) never retrieves private key material. The `NewKMSSigner` function only fetches the public key via `loadPublicKey` (lines 93-122), and the `Sign` method (lines 124-143) sends the message to the KMS `Sign` API, returning only the signature bytes. The private key remains inside the KMS hardware security modules.

### What IAM permissions are required for the KMSSigner to function?

The IAM role or user associated with the node requires two specific KMS permissions: `kms:Sign` to request signatures and `kms:GetPublicKey` to retrieve the public key during initialization. These permissions should be scoped to the specific key ARN used in the `KeyID` field of `KMSSignerOptions` to follow the principle of least privilege.

### Can I use the KMSSigner for local development without AWS credentials?

Yes, the `KMSSignerOptions` struct supports `AccessKeyID` and `SecretAccessKey` for static credential injection, allowing local development outside AWS infrastructure. Additionally, you can point the signer at a LocalStack instance by setting `EndpointURL` to `http://localhost:4566` and providing dummy credentials, as demonstrated in [`examples/generate/kms/main.go`](https://github.com/fystack/mpcium/blob/main/examples/generate/kms/main.go) lines 48-53.

### Why does the KMSSigner only support P-256 keys?

The implementation enforces `EventInitiatorKeyTypeP256` in `NewKMSSigner` (lines 35-90) to ensure compatibility with Mpcium's MPC protocol, which expects ECDSA signatures over the P-256 curve with SHA-256 hashing. This constraint is hardcoded in [`pkg/types/types.go`](https://github.com/fystack/mpcium/blob/main/pkg/types/types.go) and validated at runtime to prevent algorithm mismatches that could cause signature verification failures in the distributed signing workflow.