enclave

package module
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 10, 2022 License: Apache-2.0 Imports: 8 Imported by: 0

README

Nitro Enclaves SDK for Go

Go Report Card Go Reference

A pure Go library for utilizing AWS KMS's support for Nitro Enclaves, similar to the AWS Nitro Enclaves SDK for C.

Usage

In order to utilize KMS's support for Nitro Enclaves from Go, we must:

  1. Obtain an attestation document from the enclave's Nitro Security Module
  2. Attach that attestation document to a supported KMS call
  3. Decode and decrypt the CiphertextForRecipient field of the response

Pre-Requisites

By default, Nitro Enclaves do not provide network connectivity, or seed the enclave kernel's entropy pool. To simplify the process of getting a working enclave, we'll use Enclaver to automate building of Enclave Images.

First, if you don't have one already, create a Dockerfile to build your Go app into a Docker image:

# Build Image Stage
FROM golang:1.18-alpine AS app-builder
WORKDIR /usr/src/go-enclave-app
COPY . .
RUN go build -v -o /usr/local/bin/go-enclave-app main.go

# Release Image Stage
FROM alpine:latest AS app-container

COPY --from=app-builder /usr/local/bin/go-enclave-app /usr/local/bin/go-enclave-app
CMD ["/usr/local/bin/go-enclave-app"]

Now you can build your app into a Docker image by running:

go build . -t go-enclave-app

Now create an Enclaver manifest called enclaver.yaml alongside your Dockerfile:

version: v1
name: "go-enclave-app"
target: "go-enclave-app:enclave-latest"
sources:
  app: "go-enclave-app"
egress:
  allow:
    - 169.254.169.254
    - kms.us-west-2.amazonaws.com

This will instruct Enclaver to build a distributable Nitro Enclaves-enabled Docker image tagged as go-enclave-app:enclave-latest from the source go-enclave-app image, and cause Enclaver to permit egress traffic to the local instance metadata service and the us-west-2 KMS endpoint.

You can test this by running:

enclaver build

Obtaining an Attestation Document

The heavy-lifting for obtaining an attestation document is done, behind-the-scenes, by [Nitro Security Module Interface for Go)[https://github.com/hf/nsm].

In order for KMS to use NSM attestation documents, the attestation documents must include a public key corresponding to a private key accessible by the enclave. For simplicity, this library abstracts generation of the key and interactions with the Nitro Security Module behind the EnclaveHandle interface:

Note: this private key should never leave the enclave - it is OK for it to be ephemeral, and generate a new key for each enclave instance.

First, add this library as a dependency:

go get github.com/edgebitio/nitro-enclaves-sdk-go@latest

Then, in your code, grab a reference to the global handle, and use it to request an attestation document:

import (
	enclave "github.com/edgebitio/nitro-enclaves-sdk-go"
)

func MakeKMSRequest() error {
	enclaveHandle, err := enclave.GetOrInitializeHandle()
	if err != nil {
		return err
	}

	attestationDocument, err := enclaveHandle.Attest(enclave.AttestationOptions{})
	if err != nil {
		return err
	}

    ...
}

Making KMS Requests

Official AWS SDKs do not include support for Nitro Enclaves. They are also extensive, and well-maintained, so forking or re-implementing them is not a good option.

Instead, this library provides a drop-in replacement for github.com/aws/aws-sdk-go-v2/service/kms, which can be used trasparently via the Go Modules replace directive.

To do so, within your module run:

go mod edit -replace \
    github.com/aws/aws-sdk-go-v2/service/kms=github.com/edgebitio/nitro-enclaves-sdk-go/kms@latest

Depending on your configuration you likely need to run go mod tidy and possibly go mod vendor after this.

Now you can instantiate the KMS client and use it to make requests, with the option to include an attestation document on Decrypt, GenerateDataKey, and GenerateRandom operations using the Recipient input field:

	ctx := context.TODO()
	config, err := config.LoadDefaultConfig(ctx, config.WithRegion("us-west-2"))
	if err != nil {
		return err
	}

	kmsClient := kms.NewFromConfig(config)

	// Request a 32 byte data key from KMS, for use in AES-256 operations.
	dataKeyRes, err := kmsClient.GenerateDataKey(context.Background(), &kms.GenerateDataKeyInput{
		KeyId:   "arn:aws:kms:us-west-2:xxxxxxxxxx:key/12345678-abcd-ef12-1234-abcdef123456",
		KeySpec: types.DataKeySpecAes256,
		Recipient: &types.RecipientInfoType{
			AttestationDocument:    attestationDocument,
			KeyEncryptionAlgorithm: types.EncryptionAlgorithmSpecRsaesOaepSha256,
		},
	})
	if err != nil {
		return err
	}

Decoding and Decrypting KMS Responses

When you include a Recipient field on a Decrypt, GenerateDataKey, or GenerateRandom KMS call, KMS returns a null Plaintext field, and instead includes the plaintext data in an encrypted form in the CiphertextForRecipient field.

The EnclaveHandle interface provides the ability to decrypt this field using the private key it generated for the enclave:

	if dataKeyRes.CiphertextForRecipient == nil {
		return fmt.Errorf("CiphertextForRecipient is nil")
	}

	key, err := enclaveHandle.DecryptKMSEnvelopedKey(dataKeyRes.CiphertextForRecipient)
	if err != nil {
		return err
	}

	fmt.Printf("key: %v", key)

Now you can use key to encrypt data! To persist the encrypted data, you'll need to also persist the value of CiphertextBlob, which can be decrypted at any time using KMS.

Separation of Duties

Note that anyone with the ability to perform Decrypt calls using your KMS key will be able to decrypt the data key in CiphertextBlob, so for this to be useful you will likely want:

  1. To use a KMS Key Policy to lock down access to the KMS key such that only authorized enclave images are permitted to perform Decrypt operations. See the AWS docs for details.
  2. To ensure that only trusted users have permission to modify the KMS key policy
    • ideally these would be a completely different set of users than those who have normal production access, so that Nitro Enclaves + KMS can be used to enforce separation of duties.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type AttestationOptions

type AttestationOptions struct {
	// Nonce is an optional cryptographic nonce which may be signed as part of the attestation
	// for use by applications in preventing replay attacks.
	Nonce []byte

	// UserData is an optional opaque blob which will be signed as part of the attestation
	// for application-defined purposes.
	UserData []byte

	// NoPublicKey will prevent the defaul public key from being included in the attestation.
	NoPublicKey bool

	// PublicKey is an optional public key which will be included in the attestation. Valid types
	// are *rsa.PublicKey, *ecdsa.PublicKey, and ed25519.PublicKey.
	PublicKey any
}

type EnclaveHandle

type EnclaveHandle struct {
	// contains filtered or unexported fields
}

EnclaveHandle represents a handle to a Nitro Enclave, including the local Nitro Security Module, and an in-memory 2048 bit RSA key pair, the public key from which can be automatically included in requested attestation documents.

func GetOrInitializeHandle

func GetOrInitializeHandle() (*EnclaveHandle, error)

GetOrInitializeHandle returns a reference to the default global enclave handle, initializing that handle in the process if it has not been already. If an error occurs during initialization of the global handle (including if the error occurred during a previous initialization attempt), the error will be returned.

func MustGlobalHandle

func MustGlobalHandle() *EnclaveHandle

MustGlobalHandle returns a reference to the default enclave handle. If no handle has been initialized, one will be initialized on-demand. If an error occurs during initialization, panic.

func (*EnclaveHandle) Attest

func (enclave *EnclaveHandle) Attest(args AttestationOptions) ([]byte, error)

Attest generates and returns an attestation document from the enclave's Nitro Security Module. See AttestationOptions for more details on available options.

func (*EnclaveHandle) DecryptKMSEnvelopedKey

func (enclave *EnclaveHandle) DecryptKMSEnvelopedKey(content []byte) ([]byte, error)

DecryptKMSEnvelopedKey decrypts a KMS 'CiphertextForRecipient' response field, using the enclave's private key.

func (*EnclaveHandle) PublicKey

func (enclave *EnclaveHandle) PublicKey() *rsa.PublicKey

PublicKey reutrns a reference to the Handle's public key.

Directories

Path Synopsis
crypto
cms

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL