← Back to Blog

The identity of your code can’t be stolen

The identity of your code – a cryptographic hash of it – can’t be spoofed or stolen. This is a powerful property that can make your infrastructure extremely secure.

Let’s explore this concept by building up to making a request to Amazon’s Key Management Service (KMS) to return a decryption key that only a specific piece of code should have access to.

At build time:

  1. calculate the expected values of our cryptographic hashes
  2. record these into an access policy
  3. record these into an audit trail to find code when given a specific hash

At run time:

  1. calculate the actual values
  2. sign an attestation containing these values, anchored by trusted party

Optional, but useful at run time:

  1. provide guarantees the environment won’t be mutated from our trusted state
  2. provide guarantees that sensitive data can’t be read from memory by other processes
  3. provide protection from unwanted network egress

Each of these properties is fulfilled by AWS Nitro Enclaves, a form of secure enclave like your iPhone contains, but on a server. If you’re interested, you can read more here. The examples below will use Enclaver to build and run our Nitro Enclaves.

Calculating code identity at build time

You may be familiar with cryptographic hashes of code because they are used in Secure Boot to validate the integrity of the boot loader, Linux kernel, disk firmware, etc. When power is applied to a server, a Trusted Platform Module (TPM) chip checks that the boot loader matches the expected value, allows it to run, and then checks the next step down the line until the server is booted.

The same theory can be used for higher level processes, like Python or Java code, as long as the method for calculating and verifying the hashes is understood by all parties.

We’ll use a container in our example. It can contain anything of course, but when securing a sensitive process you want to keep it as slim and focused as possible.

enclaver build will read a source container image, build it into an enclave image, and calculate the hashes or “measurements” of our code after it is built. This is our code’s identity. You can see the output at the end as pcr0, pcr1, and pcr2:

$ enclaver build --file enclaver.yaml
 INFO  enclaver::images > latest: Pulling from edgebit-containers/containers/no-fly-list
 INFO  enclaver::images > latest: Pulling from edgebit-containers/containers/odyn
 INFO  enclaver::images > latest: Pulling from edgebit-containers/containers/nitro-cli
 INFO  enclaver::build  > starting nitro-cli build-eif in container: 40bcc4af5c0581c5fb6fc04e2aef4458b326738c7938e08df19244ec3c847972
 INFO  nitro-cli::build-eif > Start building the Enclave Image...
 INFO  nitro-cli::build-eif > Using the locally available Docker image...
 INFO  nitro-cli::build-eif > Enclave Image successfully created.
 INFO  enclaver::build      > packaging EIF into release image
Built Release Image: sha256:da0dea2c7024ba6f8f2cb993981b3c4456ab8b2d397de483d8df1b300aba7b55 (no-fly-list:enclave-latest)
EIF Info: EIFInfo {
    measurements: EIFMeasurements {
        pcr0: "85aaa37e85a0b7178bb5700a8c1ae584bf4f994996db6f18503e215cf35b65f737b19e822b3f10eb634317bd4f11deee",
        pcr1: "bcdf05fefccaa8e55bf2c8d6dee9e79bbff31e34bf28a99aa19e6b29c37ee80b214a414b7607236edf26fcb78654e63f",
        pcr2: "cb64e00fce6987d7484c18cc4c19d92ec80955d86f6b43b2d4794f9edc1a9d0200d72cf3e876566e9d888bc971413f46",
    },
}

Each of these 3 values has a well-understood method of calculating it, so different tools can arrive at the same value. We’ll use pcr0, which represents a hash of the entirety of our enclave image. When this enclave image is run, the pcr0 value should match as long as the code hasn’t been modified from its trusted state.

Running and validating code at run time

Computing and attesting the identity of our code when it is run is a job for the AWS Nitro hypervisor, which serves as our trusted third party. When it starts up our new enclave, it calculates the various hash values and freezes them into place, so our code can’t tamper with the computed values.

These are stored in a manner similar to the TPM on a physical server. The result is a cryptographic attestation, which can prove that the code is unmodified and is in its trusted state. From inside the enclave, you can request an attestation document from the hypervisor, which is signed with a public Amazon certificate authority. This document can be validated by a third party which is communicating with the code inside the enclave, to prove to them that the enclave is a safe place to send data to.

Enclaver uses a self-executing container to start and monitor an enclave, which means you can use systemd, Kubernetes or any other automation to run enclaves:

$ docker run \
    --rm \
    --detach \
    --name enclave \
    --device=/dev/nitro_enclaves:/dev/nitro_enclaves:rw \
    -p 8001:8001 \
    registry.edgebit.io/no-fly-list:enclave-latest

Code identity as an access policy

Amazon’s Key Management Service (KMS) supports inspecting a provided code attestation in operations like Decrypt, so it can be used in your key access policies. Enclaver can automatically append the attestation when making these API calls.

By far the most interesting property is that when you require a certain PCR value inside of an access policy, you are guaranteeing that the code decrypting your KMS key is inside of an enclave and therefore free from observation by any party, including the AWS administrator running the enclave. Here’s an example:

{
    "Version": "2012-10-17",
    "Id": "key-noflylist",
    "Statement": [
        {
            "Sid": "Allow decryption from within enclave",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::111122223333:foobar"
            },
            "Action": [
                "kms:Decrypt"
            ],
            "Resource": "*",
            "Condition": {
                "StringEqualsIgnoreCase": {
                    "kms:RecipientAttestation:PCR0": "85aaa37e85a0b7178bb5700a8c1ae584bf4f994996db6f18503e215cf35b65f737b19e822b3f10eb634317bd4f11deee"
                }
            }
        }
    ]
}

The second most interesting property of this scheme is that this PCR value can also be passed into an audit system to produce the exact code that is running within this enclave. This is useful in security audits, access reviews and responding to security incidents.

Separation of duties

Let’s close by looking at the separation of duties that can be achieved such that no single actor can compromise the security of this system.

Developer

The developer has access to commit code and have it built by a Continuous Integration (CI) system and optionally deployed by a Continuous Deployment (CD) system. They don’t have access to the production infrastructure, nor can they coax the build system to produce a specific cryptographic identity in order to be malicious.

Security Engineer

The security team safeguards the KMS access policies and enforces that the sensitive workflows are always deployed into secure enclaves. Major key policy changes are peer reviewed and every day changes are updated based on output from the build/CI system. An engineer can’t access any KMS data because they are only allowed to be read from within an enclave.

Infrastructure Engineer

Cloud engineers have access to production infrastructure in order to debug outages and ensure the system is running correctly. They typically have the ability to sample network traffic or peek into process memory to solve particular issues. The enclave is shielded from observation because its memory is directly allocated to it instead of being accessible from the parent EC2 VM. TLS can be terminated directly inside of the enclave, shielding any traffic from snooping. This is particularly helpful when ingesting sensitive customer data and encrypting it immediately, which prevents any plaintext from ever being processed in a manner that could be observed.

Malicious Traffic

Enclaver’s defense in depth uses an egress proxy to prevent unauthorized traffic from leaving the enclave. This protects your system from an unknown bug that is exploitable over the network from trying to upload your data to a malicious destination.

Identity that can’t be spoofed or stolen

As you can see, no single actor in this system can view the protected secrets being processed within the secure enclave. Unless you are the exact code that is trusted, you aren’t getting access to anything.

There are no credentials to steal. The parent EC2 instance communicating with your enclave can be compromised and your enclave is safe from observation.

Protecting your data via cryptographic attestation is the ultimate proactive defense.

Want to see a complete example? Check out the example Python app using Enclaver.

More from EdgeBit in the future

Enclaver fills a gap in usability and access to secure enclaves, but it doesn’t complete our mission to empower SaaS services to consume and process data securely – and in a way that maintains customer control over data.

We’re looking for design partners to influence how we tackle the wider journey of building security-centric and privacy-preserving features of their SaaS applications. We would love to chat about your ideas and test out our software.