AWS Signatures

Hello world. I am long overdue an update–so this is what I’ve been up to:

After the loss of my laptop (wrongful death by proximity to waterbottle) I’ve been installing ocaml on all the computers I can get my hands on…. First on the family desktop. Well ocaml installed alright, but opam install core_extended did not. After rebooting my computer and attempting the install again, I found that opam still had its tenderhooks in my system. But supposedly it’s okay to just delete opam‘s lock files.

But the family desktop did not pan out. I could not even compile the static site in mirage-skeleton. I’ve since commandeered my father’s laptop, where all things ocaml, opam, and mirage are working beautifully. I was able to compile a xen kernel from a static site (both the one in mirage-skeleton and one I’d created myself) and deploy it with amazon ec2. Actually the whole process was far nicer than using wordpress’s web interface to blog.

The unikernel deployment was done using amazon’s command line tools and I’ve begun working on the bindings that will allow me to do this in ocaml — which brings us to what this post is really about. Amazon requires all requests to their API to have a signature, to ensure that a request is valid or was not tampered with. ec2 supports signature versions 2 and 4. Given that the documentation for signature version 2 seemed shorter than that of signature version 4 (it has parts!), I decided to take a look at signature version 2 first.

(An Invalid!) Signature Version 2 Signing Process

To calculate a signature you need a secret key (eg wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY) and a query string (unfortunately I cannot post an example as wordpress does not approve of backslashes).

Now the documentation says,

[…] you calculate the signature by creating a hash-based message authentication code (HMAC) using either the HMAC-SHA1 or HMAC-SHA256 protocols. The HMAC-SHA256 protocol is preferred.
The resulting signature must be base-64 encoded and then URI encoded.

So I set about this task using cryptokit1. The cryptokit library has these two functions

# #require "cryptokit";;
# Cryptokit.transform_string;;
– : transform -> string -> string = <fun>
# Cryptokit.hash_string;;
– : hash -> string -> string = <fun>

After playing around in utop I discovered it also has these

# MAC.hmac_sha256;;
– : string -> hash = <fun>
# Base64.encode_compact;;
– : unit -> transform = <fun>

So naturally I created a hash function

# let my_hash = Cryptokit.hash_string (MAC.hmac_sha256 sample_secret_key);;
val my_hash : string -> string = <fun>

which I then gave the sample query string and piped to a snipped of code meant to do the base-64 encoding:

# my_hash query_string |> transform_string (Cryptokit.Base64.encode_compact ());;
– : string = "754M/4BBDhUk61bZ2zED8QPI2j3+A624SCDGda1Cfio"

Which, if you look at the documentation, is not the expected result! Clearly I’m missing something here. I just don’t yet know what…

(A Successful!) Signature Version 4 Signing Process

But I was able to implement version 4 with little trouble at all.

First things first:

# #require "cryptokit";;
# open Cryptokit;;

# let secret = "AWS4" ^ "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY";;
# let date = "20110909";;
# let reg = "us-east-1";;
# let service = "iam";;
# let signing = "aws4_request";;

# let hash_str key str = hash_string (MAC.hmac_sha256 key) str;;

Rather than using a just the query string and secret key, the version 4 signing process uses parameters unique to the request (the date, the region, etc) to create hash functions that result in values that are fed into more hash functions that result in more values. So a secret key (with AWS4 prepended) is used to create a hash function. You use this function to hash the date (eg “20110909”). This result becomes your key for the next hash. And so on.

# let kSecret = secret;;
# let kDate = hash_str kSecret date;;
# let kReg = hash_str kDate reg;;
# let kService = hash_str kReg service;;
# let kSigning = hash_str kService "aws4_request";;

The last key, kSigning is used to hash the string to sign. After hexencoding this result, you should have a signature.

# let signature = hash_str kSigning str_to_sign |> transform_string ( Hexa.encode () );;

The complete example of this process can be found here.

  1. Incidentally I cannot find any up-to-date documentation (online) for cryptokit and am most distressed by this. 

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s