nsm

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Jan 6, 2024 License: MIT Imports: 11 Imported by: 1

README

Nitro Security Module Interface for Go

Go Report Card Go Reference

This is an implementation of the AWS Nitro Security Module interface for Go. Nitro Enclaves only support Linux OSs on X86 or X64, so this package is not intended to be used on other OSs or architectures.

Usage

You can import the package like so:

import (
    "github.com/0xsequence/nsm"
)

The NSM interface implements a request/response communication model. Consult the request and response subpackages for all possible requests and responses.

You open a communication channel with the NSM device by opening a new NSM Session like so:

sess, err := nsm.OpenDefaultSession()

Use the Send function on the Session to send a new Request and receive its Response. The response struct always has one and only one field set with a non-zero value. Regardless, always check the Error field for a possible error message returned from the NSM driver.

Performance

You can open as many sessions as the OS will allow. You can send as many requests on any session, from as many goroutines as resources allow. Sending to, reading from and closing a session at the same time is not thread safe; sending requests, and reading entropy at the same time is thread safe.

Each Send and Receive reserve 16KB of memory per call. This is the way the NSM IOCTL interface is designed, so memory exhaustion may occur if you send a request or read entropy at once from many threads. Memory allocations are amortized across multiple invocations, so GC pressure should not be a significant concern.

Since the underlying transport is an IOCTL, each call performs a syscall with a blocking context switch on that thread. The NSM driver also context-switches on the Nitro hypervisor, so each request is quite expensive. Use them sparingly. For example, ask for an attestation only a couple of times within the implementation of some protocol; use the random entropy to seed a NIST SP800-90A DRBG.

Reading Entropy

Nitro Enclaves don't get access to /dev/random or /dev/urandom, but you can use the NSM to generate cryptographically secure pseudo-random numbers (entropy). A Session is also an io.Reader that asks the NSM for random bytes.

Here's an example how you can use the NSM for entropy:

import (
    "crypto/rand"
    "math/big"
    "github.com/0xsequence/nsm"
)

func generateBigPrime() (*big.Int, error) {
    sess, err := nsm.OpenDefaultSession()
    defer sess.Close()

    if nil != err {
        return nil, err
    }

    return rand.Prime(sess, 2048)
}
Obtaining an Attestation Document

Here's an example of how you can get an attestation document:

import (
    "errors"
    "github.com/0xsequence/nsm"
    "github.com/0xsequence/nsm/request"
)

func attest(nonce, userData, publicKey []byte) ([]byte, error) {
    sess, err := nsm.OpenDefaultSession()
    defer sess.Close()

    if nil != err {
        return nil, err
    }

    res, err := sess.Send(&request.Attestation{
        Nonce: nonce,
        UserData: userData,
        PublicKey: publicKey,
    })
    if nil != err {
        return nil, err
    }

    if "" != res.Error {
        return nil, errors.New(string(res.Error))
    }

    if nil == res.Attestation || nil == res.Attestation.Document {
        return nil, errors.New("NSM device did not return an attestation")
    }

    return res.Attestation.Document, nil
}

There's a full example in example/attestation.

Reference Implementation

This implementation is based on the Nitro Enclaves SDK from AWS, which is written in Rust. This implementation is a pure Go implementation of the same interface; thus you can use it to prepare reproducible builds without relying on cgo.

License

Copyright © 2021 Stojan Dimitrovski. Licensed under the MIT License. See LICENSE for more information.

Documentation

Overview

Package nsm implements the Nitro Security Module interface.

Index

Constants

This section is empty.

Variables

View Source
var DefaultOptions = Options{
	Open: func() (FileDescriptor, error) {
		return os.Open("/dev/nsm")
	},
	Syscall: syscall.Syscall,
}

DefaultOptions can be used to open the default NSM session on `/dev/nsm`.

View Source
var (
	// ErrSessionClosed is returned when the session is in a closed state.
	ErrSessionClosed error = errors.New("Session is closed")
)

Functions

This section is empty.

Types

type ErrorGetRandomFailed

type ErrorGetRandomFailed struct {
	ErrorCode response.ErrorCode
}

ErrorGetRandomFailed is an error returned when the GetRandom request as part of a `Read` has failed with an error code, is invalid or did not return any random bytes.

func (*ErrorGetRandomFailed) Error

func (err *ErrorGetRandomFailed) Error() string

Error returns the formatted string.

type ErrorIoctlFailed

type ErrorIoctlFailed struct {
	// Errno is the errno returned by the syscall.
	Errno syscall.Errno
}

ErrorIoctlFailed is an error returned when the underlying ioctl syscall has failed.

func (*ErrorIoctlFailed) Error

func (err *ErrorIoctlFailed) Error() string

Error returns the formatted string.

type FileDescriptor

type FileDescriptor interface {
	// Provide the uintptr for the file descriptor.
	Fd() uintptr

	// Close the file descriptor.
	Close() error
}

FileDescriptor is a generic file descriptor interface that can be closed. os.File conforms to this interface.

type Options

type Options struct {
	// A function that opens the NSM device file `/dev/nsm`.
	Open func() (FileDescriptor, error)

	// A function that implements the syscall.Syscall interface and is able to
	// work with the file descriptor returned from `Open` as the `a1` argument.
	Syscall func(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno)
}

Options for the opening of the NSM session.

type Session

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

A Session is used to interact with the NSM.

func OpenDefaultSession

func OpenDefaultSession() (*Session, error)

OpenDefaultSession opens a new session with the default options.

func OpenSession

func OpenSession(opts Options) (*Session, error)

OpenSession opens a new session with the provided options.

func (*Session) Close

func (sess *Session) Close() error

Close this session. It is not thread safe to Close while other threads are Read-ing or Send-ing.

func (*Session) Read

func (sess *Session) Read(into []byte) (int, error)

Read entropy from the NSM device. It is safe to call this from multiple threads that are Read-ing or Send-ing, but not Close-ing. This method will always attempt to fill the whole slice with entropy thus blocking until that occurs. If reading fails, it is probably an irrecoverable error. Each Send and Read call reserves at most 16KB of memory, so having multiple parallel sends or reads might lead to increased memory usage.

func (*Session) Send

func (sess *Session) Send(req request.Request) (response.Response, error)

Send an NSM request to the device and await its response. It safe to call this from multiple threads that are Read-ing or Send-ing, but not Close-ing. Each Send and Read call reserves at most 16KB of memory, so having multiple parallel sends or reads might lead to increased memory usage.

Directories

Path Synopsis
example
Package ioc generates the proper ioctl command numbers.
Package ioc generates the proper ioctl command numbers.
Package request contains constructs commonly used in the NSM request payload.
Package request contains constructs commonly used in the NSM request payload.
Package response contains commonly used constructs for NSM responses.
Package response contains commonly used constructs for NSM responses.

Jump to

Keyboard shortcuts

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