HSM and TPM based AWS v4 Signer

Sample procedure to encrypt AWS Access Secret Access Key using GCP Tink and a way to embed the the Key into an HSM device supporting PKCS #11.

AWS secret key and ID can be thought of as a username/password and should be carefully managed, rotated, secured as described in Best practices for managing AWS access keys. However, if you need to invoke AWS from remote systems which do not provide ambient federation (eg on GCP using OIDC tokens), then you must either utilize an AWS credentials file or set them dynamically as an environment variable.

This repo provides two ways to protect the aws secret:

  1. Wrap the secret using KMS and access it via TINK.
  2. Embed the secret into an HSM an access it via PKCS11 and Trusted Platform Module

NOTE: This code is NOT Supported by Google; its just a POC. caveat emptor

Most of the code under the aws/ folder is taken from https://github.com/aws/aws-sdk-go-v2/blob/main/aws/signer/v4/v4.go which i modified for TINK and PKCS11

In (1) you are using KMS to encrypt the Secret and save it in encrypted format. When you need to access the Secret to make it generate an AWS v4 signing request, the raw Secret is automatically decrypted by TINK using KMS and made to HMAC sign. The user will never need to “see” the secret but it is true that the key gets decoded in memory. You will also need to have access to KMS in the first place so this example is a bit convoluted. Use this if you already have access to another cloud providers KMS (eg GCP KMS), then use that decrypt the Key and then make it sign:

The encrypted Key would look like the following:

In (2), you are embedding the HMAC key INTO an HSM. When you then need to access the secret, you ask the HSM to generate an HMAC for the AWS v4 signing process. At no time does the client ever see the secret after it is embedded: the actual HMAC is done within the HSM.

Once the key is embedded into an HSM, you can access it to sign, verify, etc but the key is ever exposed

$ pkcs11-tool \
--module /usr/lib/x86_64-linux-gnu/softhsm/libsofthsm2.so \
--list-objects --pin mynewpin
Secret Key Object; unknown key algorithm 43
label: HMACKey
ID: 0100
Usage: verify
Access: sensitive

The big advantage of (2) is clear, the HSM owns the key and is not exportable: nobody will see the raw key once thats done but yet you can use it to create an AWS s4 sign.

AWS v4 Signing Protocol

The AWS S4 Signing protocol uses the AWS Secret to create an HMAC signature by first adding a small prefix to the key

The implementations for the golang aws signer is here

This repo provides an AWS Credential and Signerv4 which is intended to be used standalone

References

Some references for TINK and PKCS11:

TINK

To use TINK, we will assume you are a GCP customer with access to GCP KMS and want to access AWS via v4 Signing

First create a KMS keychain,key for Symmetric Encryption:

Now create an EncryptedKeySet with Tink, then read in that KeySet and make Tink generate an HMAC signature:

The relevant code that read in TINK is:

PKCS11

For PKCS, we will use SoftHSM which supports the PKCS mechanism to use HMAC. You should be able to use other HSM like yubikey, etc but unfortunately, TPM’s CLi i used for PKCS does not support HMAC imports: see Support TPM HMAC Import.

Anyway, first install SoftHSM and confirm you have the library linked properly (i used linux)

Now initialize the SoftHSM device:

At this point, we are ready to run the sample application which will

  1. Connect to the HSM
  2. Embed the AccessKey into the HSM
  3. Use the embedded HMAC key to create AWS V4 signature
  4. Access AWS API

note, after step2, the AWS secret is embedded inside the HSM and can only be used to make HMAC signatures.

export AWS_ACCESS_KEY_ID=AKIAUH3H6EGKERNFQLHJ
export AWS_SECRET_ACCESS_KEY=YRJ86SK5qTOZQzZTI1u-redacted
go run main.go --mode=pkcs \
--hsmLibrary /usr/lib/x86_64-linux-gnu/softhsm/libsofthsm2.so \
--awsRegion=us-east-2 -accessKeyID $AWS_ACCESS_KEY_ID \
-secretAccessKey $AWS_SECRET_ACCESS_KEY

The output of this will run STS.GetCallerIdentity

The this code does is the magic:

Conclusion

This is just a POC, caveat emptor