cap1-hmac-sha512

Signature Version 1 signing process, specification, and reference implementation

Usage no npm install needed!

<script type="module">
  import cap1HmacSha512 from 'https://cdn.skypack.dev/cap1-hmac-sha512';
</script>

README

cap1-hmac-sha512

Stability: 1 - Experimental

NPM version

Contributors

@tristanls

Contents

Overview

This module provides specification and reference implementation of Capability Signature Version 1 CAP1-HMAC-SHA512.

The signature is based on AWS Signature Version 4 with the following modifications:

  • Algorithm used is CAP1-HMAC-SHA512.
  • Credential termination string is cap1_request instead of aws4_request.
  • sha512 is used throughout instead of sha256.
  • X-Cap-Date header is used instead of X-Amz-Date or Date.
  • Request payload is not used as part of the signature.
  • Credential scope uses host instead of region and service.
  • Signing key derivation uses host instead of region and service.
  • Generated string to sign uses base64url encoded sha512 hash of canonical request instead of hex encoded sha256 hash.
  • Signature is base64url encoded instead of hex.

Installation

npm install cap1-hmac-sha512

Usage

To run the below example run:

npm run demo
"use strict";

const cap1HmacSha512 = require("../index.js");
const http = require("http"); // not https for demo only
const url = require("url");

const secrets = // hardcoded for demo only
{
    "someId": "mySecret"
};

const server = http.createServer((req, resp) =>
    {
        console.log("request received:", req.headers);
        const parsedUrl = url.parse(req.url);
        const verifyParams =
        {
            headers: req.headers,
            httpRequestMethod: req.method,
            path: parsedUrl.pathname,
            queryString: parsedUrl.query,
            secret: (keyId, callback) => callback(undefined, secrets[keyId])
        };
        cap1HmacSha512.verify(verifyParams, (error, authorized) =>
            {
                console.log("request authorized:", authorized);
                if (authorized)
                {
                    resp.statusCode = 200;
                }
                else
                {
                    resp.statusCode = 401;
                }
                resp.end();
            }
        );
        req.on("data", () => {}); // drain request
    }
);

server.listen(8888, () =>
    {
        console.log("server listening");
        const options =
        {
            host: "localhost",
            headers:
            {
                host: "localhost:8888",
                connection: "close"
            },
            method: "GET",
            path: "/somewhere?page=12",
            port: 8888
        };
        const signature = cap1HmacSha512.sign(
            {
                headers: options.headers,
                httpRequestMethod: options.method,
                key: secrets["someId"],
                keyId: "someId",
                path: "/somewhere",
                queryString: "page=12"
            }
        );
        options.headers.authorization = signature.authorization;
        options.headers["x-cap-date"] = signature["x-cap-date"];
        console.log("client request:", options);
        http.request(options, resp =>
            {
                console.log("response status code:", resp.statusCode);
                resp.on("data", () => {}); // drain response
                delete options.headers["x-cap-date"];
                http.request(options, resp =>
                    {
                        console.log("response status code:", resp.statusCode);
                        resp.on("data", () => {}); // drain response
                        process.exit(0);
                    }
                ).end();
            }
        ).end();
    }
);

Tests

npm test

Documentation

Cap1HmacSha512

Public API

sign(params)

  • params: Object Signature parameters.
    • headers: Object HTTP request headers.
    • httpRequestMethod: String (Default: "GET") HTTP request method.
    • key: String Secret to sign with corresponding to provided keyId.
    • keyId: String Id of the secret to sign with.
    • path: String (Default: "/") HTTP request path.
    • queryString: String (Default: "") HTTP request query string.
  • Return: Object Result.
    • algorithm: String Algorithm used, CAP1-HMAC-SHA512.
    • authorization: String HTTP Authorization header contents.
    • credential: String Credential used for signing.
    • x-cap-date: String HTTP X-Cap-Date header contents.
    • signedHeaders: String List of headers used for signing.
    • signature: String base64url encoded signature.

Calculates the CAP1-HMAC-SHA512 signature given provided params. The authorization parameter from the result can be used directly as the Authorization header in HTTP request.

If params.headers["X-Cap-Date"] is not provided, an X-Cap-Date header will be generated and used as part of the signature. For the HTTP request to be valid, the x-cap-date field from the result must be used as the X-Cap-Date header in the HTTP request.

verify(params, callback)

  • params: Object Verification parameters.
    • headers: Object HTTP request headers.
    • httpRequestMethod: String HTTP request method.
    • path: String HTTP request path.
    • queryString: String HTTP request query string.
    • secret: Function (keyId, callback) => {} Function to retrieve key material corresponding to provided keyId.
      • keyId: String Key id from CAP1-HMAC-SHA512 signature to retrieve key material for.
      • callback: Function (error, key) => {} Callback to call with error or key material.
  • callback: Function (error, authorized) => {}
    • error: Error Error, if any.
    • authorized: Boolean true if signature is verified, false otherwise.

Extracts keyId from Authorization header, retrieves corresponding key material via secret callback, and calculates CAP1-HMAC-SHA512 signature given provided params. If signature is verified, it calls callback with authorized=true, otherwise, callback is called with authorized=false or an error.

Releases

Current releases.

Policy

We follow the semantic versioning policy (semver.org) with a caveat:

Given a version number MAJOR.MINOR.PATCH, increment the:

MAJOR version when you make incompatible API changes,
MINOR version when you add functionality in a backwards-compatible manner, and
PATCH version when you make backwards-compatible bug fixes.

caveat: Major version zero is a special case indicating development version that may make incompatible API changes without incrementing MAJOR version.