I am designing a RESTful API in python. I want to use HMAC authentication. I could not find any suitable HMAC libraries, so I am rolling my own. However I want to use a well-known standard such as AWS HMAC authentication v4.
While coding an AWS-compliant client and server HMAC auth implementation, I noticed some uncommon aspects of the standard's design. I fail to fully grasp several design choices that were made.
Question 1: Why does AWS use a complex signing key (produced via HMAC) instead of the shared secret itself?
Task 3: Calculate the AWS Signature Version 4 describes:
Before you calculate a signature, you derive a signing key from your AWS secret access key. (You don't just use your secret access key to sign the request.) You then use the signing key and the string to sign that you created in Task 2: Create a String to Sign for Signature Version 4 as the inputs to a keyed hash function. The hex-encoded result from the keyed hash function is the signature.
To calculate a signature
Derive your signing key. To do this, you use your secret access key to create a series of hash-based message authentication codes (HMACs) as shown by the following pseudocode, where HMAC(key, data) represents an HMAC-SHA256 function that returns output in binary format. The result of each hash function becomes input for the next one.
Pseudocode for deriving a signing key:
kSecret = Your AWS Secret Access Key
kDate = HMAC("AWS4" + kSecret, Date)
kRegion = HMAC(kDate, Region)
kService = HMAC(kRegion, Service)
kSigning = HMAC(kService, "aws4_request")
Question 2: I do not understand the purpose of the 'credential scope' string.
In steps 2-3 of Task 2 (Create a String to Sign for Signature Version 4), a date and credential scope value are appended to the string that is signed. What is the purpose of these values?
The standard already signs the UTC timestamp in the x-amz-date
header, as part of the canonical request that was prepared in Task 1. The same is true of the region specifier (us-east-1), since it should presumably be reflected in the Host:
header.
Append the request date value, followed by a newline character. The date is specified by using the ISO8601 Basic format via the x-amz-date header in the YYYYMMDD'T'HHMMSS'Z' format. This value must match the value you used in any previous steps.
20110909T233600Z\n
Append the credential scope value, followed by a newline character. This value is a string that includes the date (just the date, not the date and time), the region you are targeting, the service you are requesting, and a termination string ("aws4_request") in lowercase characters. The region and service name strings must be UTF-8 encoded.
20110909/us-east-1/iam/aws4_request\n
The date must be in the YYYYMMDD format. Note that the date does not include a time value.