0

We are developing an online course website.
Courses have audio and text (no video).
Audio files are stored on Amazon S3 and delivered via AWS CloudFront.

Every time a user wants to play a course audio file,
website (server side) sends a request to CloudFront to get the audio file.
Each request also includes a signature and a random number.

We use PHP hash_hmac to generate the signature as follows:

$signature = hash_hmac('sha256', $random_number, $secret, true);

Both sides (website and CloudFront function) have the secret key hard-coded.

Once CloudFront receives the request,
a "CloudFront Function" (new AWS feature) is validating the token.
CloudFront Function can only use JavaScript (not NodeJS nor Python).
https://aws.amazon.com/blogs/aws/introducing-cloudfront-functions-run-your-code-at-the-edge-with-low-latency-at-any-scale/

We want CloudFront function to simply run hash_hmac() using JavaScript
and then compare the signatures but we could not find an equivalent JS function.

We checked extensively on StackOverflow but could not find an equivalent JS function.
Our findings:
Google`s Crypto-JS library is deprecated.
NodeJS Crypto module cannot be used since we use plain JavaScript.

Can someone PLEASE share a link or a code snippet on how to run hash_hmac on JavaScript?

  • Does this answer your question? [How can I hash a string with SHA256 in JS?](https://stackoverflow.com/questions/59777670/how-can-i-hash-a-string-with-sha256-in-js) – NineBerry May 10 '21 at 17:12
  • @biesior I’m not sure this would qualify as “client side” as the script is executed on a CloudFront Edge Location, not in the requesting browser. – esqew May 10 '21 at 17:20

2 Answers2

2

Yes there is a native API for that:

(async function () {
  const someData = 'Very genuine data';

  const key = await window.crypto.subtle.generateKey({
    name: "HMAC",
    hash: {
      name: "SHA-256"
    }
  },
      true,
      ["sign", "verify"]);

  let enc = new TextEncoder().encode(someData);

  const signature = await crypto.subtle.sign('HMAC', key, enc);

  console.log(signature); // ArrayBuffer(32)
  
  const valid = await crypto.subtle.verify('HMAC', key, signature, enc);
  
  console.log(valid);
  
})();
Guerric P
  • 30,447
  • 6
  • 48
  • 86
  • Indeed as I read your question again, it makes no sense to use HMAC since the key (supposedly private) is actually made public because included in the frontend – Guerric P May 10 '21 at 17:39
0

So one way is to use JS crypto.subtle.sign as @Guerric P mentioned above.

It turns out there is another way to achieve it.
After reading the complete CloudFront developer guide,
I found the following:

NodeJS has a great module called Crypto.

In general, we cannot add a NodeJS module to JavaScript code.

However, CloudFront Function (who use plain JS only) supports also NodeJS Crypto module:
https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/functions-javascript-runtime-features.html#writing-functions-javascript-features-builtin-modules-crypto

The cryptographic module (crypto) provides standard hashing and hash-based message authentication code (HMAC) helpers. You can load the module using require('crypto'). The module provides the following methods that behave exactly as their Node.js counterparts.