# AWS Nitro Secure Module (NSM) interface in native Python 
This library offers a native Python interface to the `/dev/nsm` device in AWS Nitro Enclaves.

## Installation
To install `aws_nsm_interface`, run:

```
pip install aws_nsm_interface
```

### Requirements
* To install: python>=3.6
* To run: a Python application running in an AWS Nitro Enclave

## Quickstart
```python
import aws_nsm_interface

file_desc = aws_nsm_interface.open_nsm_device()

rand_bytes = aws_nsm_interface.get_random(file_desc, 12) # Get 12 random bytes from /dev/nsm
print(rand_bytes)

public_rsa_key = b'1234' # An RSA public key exported as DER

attestation_doc = aws_nsm_interface.get_attestation_doc(
    file_desc,
    public_key=public_rsa_key
)['document']

attestation_doc_b64 = base64.b64encode(attestation_doc).decode('utf-8')

aws_nsm_interface.close_nsm_device(file_desc)

# Use `attestation_doc_b64` in your AWS KMS Decrypt call
```

## Public methods
The Python AWS NSM interface provides the following functions:

```python
# open_nsm_device() returns a file object for /dev/nsm
open_nsm_device() -> typing.TextIO
# close_nsm_device() closes the file object
close_nsm_device(file_handle: typing.TextIO) -> None


# All of the functions below raise an IoctlError in case of an exception.

# Generate up to 256 random bytes with /dev/nsm. Returns the raw bytes.
get_random(file_handle: typing.TextIO, length: int = 32) -> bytes
# Example output: b'se\xb7\x05O<:\x07W\x8cfn'

# Return an attestation doc generated by /dev/nsm. `user_data`, `nonce` and
# `public_key` are all binary (bytes) and optional.
get_attestation_doc(
    file_handle: typing.TextIO,
    user_data: bytes = None,
    nonce: bytes = None,
    public_key: bytes = None
) -> dict
# Example output: {'document': b'\x84D\xa1\x018"\xa0Y ... 
# \xeb|\x1b\xf6\xb6\x95\xb4\x9c[+x\x8b'}


# Describe the NSM and known PCRs.
describe_nsm(file_handle: typing.TextIO) -> dict
# Example output: {'version_major': 1, 'version_minor': 0, 'version_patch': 0,
# 'module_id': 'i-00c89f181802cdef4-enc0175cd0dcee36866', 'max_pcrs': 32,
# 'locked_pcrs': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
# 'digest': 'SHA384'}

# Extend a PCR at the given index. Raises an IoctlError if the PCR is locked.
# Returns the new data for the PCR.
extend_pcr(file_handle: typing.TextIO, index: int, data: bytes) -> dict
# Example output: {'data': b'\x9c\t\x15Rk\xb6(R~+mi\xabs ...
# \xf6j\xf8\xbf\xa3*A\x19\xc0\x0cr\x15\xdf\x1b'}

# Returns a dictionary with the lock status and PCR data for the PCR at the 
# given index (index 0 returns PCR0, and so on).
describe_pcr(file_handle: typing.TextIO, index: int) -> dict
# Example output: {'lock': False, 'data': b'\x9c\t\x15Rk\xb6(R~ ...
# \x15\xdf\x1b'}

# lock_pcr() locks the PCR at the given index.
lock_pcr(file_handle: typing.TextIO, index: int) -> bool

# lock_pcrs() locks the PCRs from 0 up to the given lock_range.
# For example: a range of 5 will lock PCRs [0, 1, 2, 3, 4] - a range
# of 5 starting at 0.
lock_pcrs(file_handle: typing.TextIO, lock_range: int) -> bool
```
