Skip to content

cisco/node-kms

Repository files navigation

node-kms

A JavaScript implementation of Key Management Service (KMS) for current web browsers and node.js-based servers. The KMS API is described in [draft-abiggs-saag-key-management-service-02].

Installing

To install the latest from NPM:

  npm install node-kms

Or to install a specific release:

  npm install [email protected]

Alternatively, the latest unpublished code can be installed directly from the repository:

  npm install git+ssh://[email protected]:cisco/node-kms.git

Basics

Require the library as normal:

var KMS = require('node-kms');

This library uses Promises for many operations.

This library supports Browserify. To use in a web browser, require('node-kms') and bundle with the rest of your app.

KeyObjects

A KMS KeyObject wraps a JSON Web Key (JWK) to provide more semantics: a URI to locate it; the creating user and client; the date/time of when a key is created, bound, and/or expires; and the owning resource (once bound).

Creating

To create an empty KeyObject:

var keyobj = new KMS.KeyObject();

None of the KMS.KeyObject properties are set.

Alternatively, to create a KeyObject from a JSON or POJO representation:

// {input} is one of:
// *  a JSON object (where date/times are RFC3339-encoded Strings)
// *  a POJO (where date/times are Date objects)
var keyobj = new.KeyObject(input);

Importing/Exporting

NOTE: The JSON representation includes all properties for a KeyObject, including the full JWK (if present). This can expose secret key material if not carefully handled; do not save to durable storage without protecting it (e.g., encrypting to a JWE).

To import a KeyObject from a JSON object:

// {input} is one of:
// *  a JSON object (where date/times are RFC3339-encoded Strings)
// *  a POJO (where date/times are Date objects)
// *  an existing KeyObject instance
keyobj = KMS.fromObject(input);

In the case where input is already a KeyObject, it is returned as-is.

To export a KeyObject to a JSON object:

var output = keyobj.toJSON();

Obtaining a node-jose Key

To convert the jwk property of a KeyObject to a node-jose Key (to use for encryption or signatures):

var jwk;
keyobj.asKey().
    then(function(result) {
      // {result} is a jose.JWK.Key
      jwk = result;
    });

If jwk is not set on the KeyObject, the returned Promise is rejected.

Contexts

The KMS.Context holds onto information necessary to wrap Requests and unwrap Responses.

Creating and Initializing

To create an empty Context:

var kmsCtx = new KMS.Context();

None of the Context properties are set.

To finish initializing the Context, set the clientInfo and serverInfo properties:

// {clientId} is a String containing an identifier for the client or session
// {userId} is a String containing the user's identifier
// {oauth2token} is a String containing an OAuth2 Bearer token
kmsCtx.clientInfo = {
  clientId: clientId,
  credential: {
    userId: userId,
    bearer: oauth2token
  }
};
// {serverPublicKey} is a JWK JSON object
kmsCtx.serverInfo = {
  key: serverPublicKey
};

Generating an Ephemeral EC Key

To create a KeyObject representing the local ECDH key:

kmsCtx.createECDHKey().
    then(function(result) {
      // {result} is a KMS.KeyObject wrapping a "EC" JWK
      kmsCtx.ephemeralKey = result;
    })

Deriving an Ephemeral Shared Key

To derive an ephemeral shared key -- such as the result of the ECDHE handshake:

// {remoteECDH} is a KMS.KeyObject wrapping a "EC" JWK
kmsCrx.deriveEphemeralKey(remoteECDH).
    then(function(result) {
      // {result} is a KMS.KeyObject wrapping a "oct" JWK
      kmsCtx.ephemeralKey = result;
    });

Requests

The KMS.Request embodies a single request from a client to the KMS.

A Request instance has the following (read/write) properties:

  • body -- the full (plaintext) JSON to be sent to the KMS
  • requestId -- the unique id for this request
  • uri -- the URI of the request (e.g., "/ecdhe/", "/resources", etc.)
  • method -- the method (verb) for the request (e.g., "create", "retrieve", etc.)
  • wrapped -- the wrapped (encrypted) body

When a new body is set, the previous requestId, method, and uri are remembered, overwriting any new values that might have been in the provided JSON.

Creating

To create an empty request:

var request = new KMS.Request();

To create a request starting with a constructed body:

// {input} is a JSON object representing the request 
var request = new KMS.Request(input);

Wrapping

To wrap (encrypt) the Request into a JWE for transmitting to a KMS server, using an ephemeral shared key:

var output;
request.wrap(kmsCtx).
    then(function(result) {
      // {result} is a String of the JWE in the Compact Serialization
      // request.wrapped is also set to {result}
      output = result;
    });

Responses

The KMS.Response embodies a single response to a client from the KMS.

A Response instance has the following (read/write) properties:

  • body -- the full (plaintext) JSON received from the KMS
  • requestId -- the id for the corresponding request
  • status -- the status code of the response
  • reason -- the string reason (if any)
  • wrapped -- the protected (encrypted or signed) body

Creating

To create an empty KMS.Response:

var response = new KMS.Response();

To creat a KMS.Response with a received wrapped body:

// {input} is a String of the JWE (or JWS) using the Compact Serialization
var response = new KMS.Response(input);

Unwrapping

To unwrap a response into the plaintext body:

var input;
response.unwrap(kmsCtx).
    then(function(result) {
      // {result} is the plaintext JSON object
      // response.body is also set to {result}
      input = result;
    });