What is keygrip?
The keygrip npm package is used to manage and rotate keys used in signing and verifying data, such as cookies. It provides a robust way to handle key rotation without needing to re-sign existing data with new keys. This is particularly useful for applications that need to maintain integrity and authenticity of data over time, even as security requirements evolve.
What are keygrip's main functionalities?
Signing data
This feature allows you to sign data using a list of keys. The most recent key is used for signing. This is useful for creating verifiable tokens or cookies that can be validated later.
const Keygrip = require('keygrip');
const keys = ['SEKRIT1', 'SEKRIT2'];
const keygrip = new Keygrip(keys);
const data = 'some data to sign';
const hash = keygrip.sign(data);
Verifying signed data
This feature checks if a given hash matches the signed data using any of the keys in the key list. It's useful for authentication processes where you need to ensure data integrity and authenticity.
const Keygrip = require('keygrip');
const keys = ['SEKRIT1', 'SEKRIT2'];
const keygrip = new Keygrip(keys);
const data = 'some data to sign';
const hash = keygrip.sign(data);
const isValid = keygrip.verify(data, hash);
Index of used key
This feature returns the index of the key that was used to sign the data. This is particularly useful for determining which of the keys in the current list was used, aiding in decisions about key rotation and management.
const Keygrip = require('keygrip');
const keys = ['SEKRIT1', 'SEKRIT2'];
const keygrip = new Keygrip(keys);
const data = 'some data to sign';
const hash = keygrip.sign(data);
const keyIndex = keygrip.index(data, hash);
Other packages similar to keygrip
jsonwebtoken
jsonwebtoken is a popular npm package for handling JSON Web Tokens (JWT). It can sign tokens, verify them, and decode them. Unlike keygrip, jsonwebtoken is specifically designed for JWTs and includes additional features for handling token expiration and audience checking.
cookie-signature
cookie-signature is used for signing and unsigning cookies. It is similar to keygrip in that it handles the signing of data to ensure authenticity. However, cookie-signature does not support key rotation, which is a core feature of keygrip.
keygrip
Keygrip is a node.js module for signing and verifying data (such as cookies or URLs) through a rotating credential system, in which new server keys can be added and old ones removed regularly, without invalidating client credentials.
Install
$ npm install keygrip
API
keys = new Keygrip([keylist], [hmacAlgorithm], [encoding])
This creates a new Keygrip based on the provided keylist, an array of secret keys used for SHA1 HMAC digests. keylist
is obligatory. hmacAlgorithm
defaults to 'sha1'
and encoding
defaults to 'base64'
.
Note that the new
operator is also optional, so all of the following will work when Keygrip = require("keygrip")
:
keys = new Keygrip(["SEKRIT2", "SEKRIT1"])
keys = Keygrip(["SEKRIT2", "SEKRIT1"])
keys = require("keygrip")()
keys = Keygrip(["SEKRIT2", "SEKRIT1"], 'sha256', 'hex')
keys = Keygrip(["SEKRIT2", "SEKRIT1"], 'sha256')
keys = Keygrip(["SEKRIT2", "SEKRIT1"], undefined, 'hex')
The keylist is an array of all valid keys for signing, in descending order of freshness; new keys should be unshift
ed into the array and old keys should be pop
ped.
The tradeoff here is that adding more keys to the keylist allows for more granular freshness for key validation, at the cost of a more expensive worst-case scenario for old or invalid hashes.
Keygrip keeps a reference to this array to automatically reflect any changes. This reference is stored using a closure to prevent external access.
keys.sign(data)
This creates a SHA1 HMAC based on the first key in the keylist, and outputs it as a 27-byte url-safe base64 digest (base64 without padding, replacing +
with -
and /
with _
).
keys.index(data, digest)
This loops through all of the keys currently in the keylist until the digest of the current key matches the given digest, at which point the current index is returned. If no key is matched, -1
is returned.
The idea is that if the index returned is greater than 0
, the data should be re-signed to prevent premature credential invalidation, and enable better performance for subsequent challenges.
keys.verify(data, digest)
This uses index
to return true
if the digest matches any existing keys, and false
otherwise.
Example
var assert = require("assert")
, Keygrip = require("keygrip")
, keylist, keys, hash, index
keylist = ["SEKRIT3", "SEKRIT2", "SEKRIT1"]
keys = Keygrip(keylist)
hash = keys.sign("bieberschnitzel")
assert.ok(/^[\w\-]{27}$/.test(hash))
index = keys.index("bieberschnitzel", hash)
assert.equal(index, 0)
matched = keys.verify("bieberschnitzel", hash)
assert.ok(matched)
index = keys.index("bieberschnitzel", "o_O")
assert.equal(index, -1)
keylist.unshift("SEKRIT4")
keylist.pop()
index = keys.index("bieberschnitzel", hash)
assert.equal(index, 1)
hash = keys.sign("bieberschnitzel")
License
MIT