18

I'm attempting to write an Amazon Product Advertising API client in Elixir. The developer guide describes the process for signing an API request in which the an HMAC-SHA26 hash must be created using the request and the "Secret Access Key." This is the function I wrote to handle signing the request:

defp sign_request(url) do
  url_parts = URI.parse(url)
  request = "GET\n" <> url_parts.host <> "\n" <> url_parts.path <> "\n" <> url_parts.query
  url <> "&Signature=" <> :crypto.hmac(:sha256, 'ThisIsMySecretAccessKey', request)
end

The url passed into the function looks something like this: http://webservice.amazon.com/onca/xml?AssociateTag=ThisIsMyAssociateTag&AWSAccessKeyId=ThisIsMyAWSAccessKeyId&Keywords=stuff&Operation=ItemSearch&SearchIndex=Apparel&Service=AWSECommerceService&Timestamp=2014-11-22T12%3A00%3A00Z&Validate=True&Version=2013-08-01

The issue I'm having is that, while:crypto.hmac/3 returns a binary, that binary is not a string; passing the return value to String.valid?/1 returns false. So, I am unable to concatenate the return value onto the end of the url string to sign the request.

Am I using :crypto.hmac/3 incorrectly? Is there something I'm missing? Is there an alternate way I should be going about this?

Zach Garwood
  • 183
  • 1
  • 1
  • 8
  • 1
    You may also want to look into using string interpolation in Elixir. http://elixir-lang.org/getting_started/2.html#2.4-strings May simplify your code a bit. – Onorio Catenacci Nov 24 '14 at 16:27

1 Answers1

37

When you using :crypto.hmac/3 its return base 16 integer in binary format, your problem could be solved like this:

:crypto.mac(:hmac, :sha256, "key", "The quick brown fox jumps over the lazy dog")
|> Base.encode16

This is match example from https://en.wikipedia.org/wiki/Hash-based_message_authentication_code#Examples_of_HMAC_.28MD5.2C_SHA1.2C_SHA256.29

Community
  • 1
  • 1
Igor Kapkov
  • 3,301
  • 2
  • 16
  • 19
  • 6
    Or maybe use Base.encode16 (:crypto.hmac(:sha256, "key", "The quick brown fox jumps over the lazy dog") |> Base.encode16) – sasajuric Nov 23 '14 at 12:15
  • I used the solution that @sasajuric proposed in his comment because it is one less line and it's easier to read. – Zach Garwood Jan 22 '15 at 20:53
  • 7
    That's not the same as what's in the answer. The difference is the same as between `hmac_bytes |> Base.encode16` and `hmac_bytes |> Base.encode16 |> Base.encode16`. Encoding twice seems out of place. – pkoch Aug 31 '17 at 16:18
  • 5
    This will be `:crypto.mac(:hmac, :sha256, priv_key, content) |> Base.encode16` in the future according to https://erlang.org/doc/apps/crypto/new_api.html#the-new-api – Joe Eifert Mar 28 '21 at 17:56