simple-crypt

Simple and easy-to-use encryption and signing module

npm install simple-crypt
1 downloads in the last week
15 downloads in the last month

simple-crypt   Build Status Coverage Status NPM version

Javascript library for signing and encrypting data.

  • Consistent API across Node.js and browser.
  • On Node.js wraps crypto and ursa modules.
  • On browser wraps SlowAES, pbkdf2.js, CryptoJS, jsrsasign and js-rsa-pem.
  • Hard-coded to HMAC-SHA-256 for symmetric signing.
  • Hard-coded to RSA-SHA-256 with RSASSA-PSS encoding for asymmetric signing.
  • Hard-coded to AES-128-CBC for symmetric key encryption (with optional SHA-256 checksum).
  • Hard-coded to RSA, RSAES-OAEP encoding and AES-128-CBC for asymmetric encryption (with optional SHA-256 checksum).
  • Verification and decryption operations included.
  • Support for deriving signing and encryption key from a password using PBKDF2-SHA1.
  • JSON encoding of data by default.
  • Unit tests, including NIST test vectors and tests for interoperability between Node.js and browser (using PhantomJS).

Example:

var Crypt = require('simple-crypt').Crypt;
var data = { device_id: 'temperature_sensor0', value: 15.765 };

Crypt.make('my signing key', function (err, signer)
{
    signer.sign(data, function (err, signed)
    {
        Crypt.make(this.get_key(), function (err, verifier)
        {
            verifier.verify(signed, function (err, verified)
            {
                assert.deepEqual(verified, data);
            });
        });
    });
});

The API is described here.

Please feel free to make any comments (or pull requests), especially if you notice something wrong!

Installation

Node.js:

npm install simple-crypt

Browser:

<script type="text/javascript" src="dist/simple-crypt-deps.js"></script>
<script type="text/javascript" src="dist/simple-crypt.js"></script>

More Examples

Encryption

Crypt.make(crypto.randomBytes(Crypt.get_key_size()), function (err, encrypter)
{
    encrypter.encrypt(data, function (err, encrypted)
    {
        Crypt.make(this.get_key(), function (err, decrypter)
        {
            decrypter.decrypt(encrypted, function (err, decrypted)
            {
                assert.deepEqual(decrypted, data);
            });
        });
    });
});

Asymmetric operation

var priv_pem = "-----BEGIN RSA PRIVATE KEY-----\nMIIEogIBAAKCAQEA4qiw8PWs7PpnnC2BUEoDRcwXF8pq8XT1/3Hc3cuUJwX/otNe\nfr/Bomr3dtM0ERLN3DrepCXvuzEU5FcJVDUB3sI+pFtjjLBXD/zJmuL3Afg91J9p\n79+Dm+43cR6wuKywVJx5DJIdswF6oQDDzhwu89d2V5x02aXB9LqdXkPwiO0eR5s/\nxHXgASl+hqDdVL9hLod3iGa9nV7cElCbcl8UVXNPJnQAfaiKazF+hCdl/syrIh0K\nCZ5opggsTJibo8qFXBmG4PkT5YbhHE11wYKILwZFSvZ9iddRPQK3CtgFiBnXbVwU\n5t67tn9pMizHgypgsfBoeoyBrpTuc4egSCpjsQIDAQABAoIBAF2sU/wxvHbwAhQE\npnXVMMcO0thtOodxzBz3JM2xThhWnVDgxCPkAhWq2X0NSm5n9BY5ajwyxYH6heTc\np6lagtxaMONiNaE2W7TqxzMw696vhnYyL+kH2e9+owEoKucXz4QYatqsJIQPb2vM\n0h+DfFAgUvNgYNZ2b9NBsLn9oBImDfYueHyqpRGTdX5urEVtmQz029zaC+jFc7BK\nY6qBRSTwFwnVgE+Td8UgdrO3JQ/0Iwk/lkphnhls/BYvdNC5O8oEppozNVmMV8jm\n61K+agOh1KD8ky60iQFjo3VdFpUjI+W0+sYiYpDb4+Z9OLOTK/5J2EBAGim9siyd\ngHspx+UCgYEA9+t5Rs95hG9Q+6mXn95hYduPoxdFCIFhbGl6GBIGLyHUdD8vmgwP\ndHo7Y0hnK0NyXfue0iFBYD94/fuUe7GvcXib93heJlvPx9ykEZoq9DZnhPFBlgIE\nSGeD8hClazcr9O99Fmg3e7NyTuVou+CIublWWlFyN36iamP3a08pChsCgYEA6gvT\npi/ZkYI1JZqxXsTwzAsR1VBwYslZoicwGNjRzhvuqmqwNvK17dnSQfIrsC2VnG2E\nUbE5EIAWbibdoL4hWUpPx5Tl096OjC3qBR6okAxbVtVEY7Rmv7J9RwriXhtD1DYp\neBvo3eQonApFkfI8Lr2kuKGIgwzkZ72QLXsKJiMCgYBZXBCci0/bglwIObqjLv6e\nzQra2BpT1H6PGv2dC3IbLvBq7hN0TQCNFTmusXwuReNFKNq4FrB/xqEPusxsQUFh\nfv2Il2QoI1OjUE364jy1RZ7Odj8TmKp+hoEykPluybYYVPIbT3kgJy/+bAXyIh5m\nAv2zFEQ86HIWMu4NSb0bHQKBgETEZNOXi52tXGBIK4Vk6DuLpRnAIMVl0+hJC2DB\nlCOzIVUBM/VxKvNP5O9rcFq7ihIEO7SlFdc7S1viH4xzUOkjZH2Hyl+OLOQTOYd3\nkp+AgfXpg8an4ujAUP7mu8xaxns7zsNzr+BCgYwXmIlhWz2Aiz2UeL/IsfOpRwuV\n801xAoGADQB84MJe/X8xSUZQzpn2KP/yZ7C517qDJjComGe3mjVxTIT5XAaa1tLy\nT4mvpSeYDJkBD8Hxr3fB1YNDWNbgwrNPGZnUTBNhxIsNLPnV8WySiW57LqVXlggH\nvjFmyDdU5Hh6ma4q+BeAqbXZSJz0cfkBcBLCSe2gIJ/QJ3YJVQI=\n-----END RSA PRIVATE KEY-----";
var pub_pem = "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4qiw8PWs7PpnnC2BUEoD\nRcwXF8pq8XT1/3Hc3cuUJwX/otNefr/Bomr3dtM0ERLN3DrepCXvuzEU5FcJVDUB\n3sI+pFtjjLBXD/zJmuL3Afg91J9p79+Dm+43cR6wuKywVJx5DJIdswF6oQDDzhwu\n89d2V5x02aXB9LqdXkPwiO0eR5s/xHXgASl+hqDdVL9hLod3iGa9nV7cElCbcl8U\nVXNPJnQAfaiKazF+hCdl/syrIh0KCZ5opggsTJibo8qFXBmG4PkT5YbhHE11wYKI\nLwZFSvZ9iddRPQK3CtgFiBnXbVwU5t67tn9pMizHgypgsfBoeoyBrpTuc4egSCpj\nsQIDAQAB\n-----END PUBLIC KEY-----";

Crypt.make(priv_pem, function (err, signer)
{
    signer.sign(data, function (err, signed)
    {
        Crypt.make(pub_pem, function (err, verifier)
        {
            verifier.verify(signed, function (err, verified)
            {
                assert.deepEqual(verified, data);
            });
        });
    });
});

Crypt.make(pub_pem, function (err, encrypter)
{
    encrypter.encrypt(data, function (err, encrypted)
    {
        Crypt.make(priv_pem, function (err, decrypter)
        {
            decrypter.decrypt(encrypted, function (err, decrypted)
            {
                assert.deepEqual(decrypted, data);
            });
        });
    });
});

Passwords

var pw_info = { password: 'P@ssW0rd!', iterations: 10000 };

Crypt.make(pw_info, function (err, signer)
{
    signer.sign(data, function (err, signed)
    {
        var salted = Object.create(pw_info);
        salted.salt = this.get_key().salt;
        Crypt.make(salted, function (err, verifier)
        {
            verifier.verify(signed, function (err, verified)
            {
                assert.deepEqual(verified, data);
            });
        });
    });
});

Crypt.make(pw_info, function (err, encrypter)
{
    encrypter.encrypt(data, function (err, encrypted)
    {
        var salted = Object.create(pw_info);
        salted.salt = this.get_key().salt;
        Crypt.make(salted, function (err, decrypter)
        {
            decrypter.decrypt(encrypted, function (err, decrypted)
            {
                assert.deepEqual(decrypted, data);
            });
        });
    });
});

Conditional operation

Crypt.make('some key', function (err, signer)
{
    signer.maybe_sign(false, data, function (err, signed)
    {
        assert.equal(signed.signed, false);
        Crypt.make(this.get_key(), function (err, verifier)
        {
            verifier.maybe_verify(signed, function (err, verified)
            {
                assert.deepEqual(verified, data);
            });
        });
    });
});

Crypt.make(crypto.randomBytes(Crypt.get_key_size()), function (err, encrypter)
{
    encrypter.maybe_encrypt(true, data, function (err, encrypted)
    {
        assert.equal(encrypted.encrypted, true);
        Crypt.make(this.get_key(), function (err, decrypter)
        {
            decrypter.maybe_decrypt(encrypted, function (err, decrypted)
            {
                assert.deepEqual(decrypted, data);
            });
        });
    });
});

Dynamic key retrieval

var pub_pems = { temperature_sensor0: pub_pem };
var priv_pems = { temperature_sensor0: priv_pem };

Crypt.make().maybe_sign(data, function (err, signed)
{
    assert.equal(signed.signed, true);
    Crypt.make().maybe_verify(signed, function (err, verified)
    {
        assert.deepEqual(verified, data);
    }, function (cb, device_id)
    {
        cb(null, pub_pems[device_id]);
    });
}, function (device_id, cb)
{
    cb(null, priv_pems[device_id], device_id);
}, data.device_id);

Crypt.make().maybe_encrypt(data, function (err, encrypted)
{
    assert.equal(encrypted.encrypted, true);
    Crypt.make().maybe_decrypt(encrypted, function (err, decrypted)
    {
        assert.deepEqual(decrypted, data);
    }, function (cb, device_id)
    {
        cb(null, priv_pems[device_id]);
    });
}, function (device_id, cb)
{
    cb(null, pub_pems[device_id], device_id);
}, data.device_id);

Sign-encrypt-sign

Crypt.sign_encrypt_sign(priv_pem, pub_pem, data, function (err, data_out)
{
    Crypt.verify_decrypt_verify(priv_pem, pub_pem, data_out, function (err, data_in)
    {
        assert.deepEqual(data_in, data);
    });
});

JSON-less encoding

Crypt.make('some signing key', { json: false }, function (err, signer)
{
    signer.sign(new Buffer('"hello"'), function (err, signed)
    {
        this.verify(signed, function (err, verified)
        {
            assert.equal(verified, '"hello"');
        });
    });
});

Licence

MIT

Tests

grunt test

Lint

grunt lint

Code Coverage

grunt coverage

Instanbul results are available here.

Coveralls page is here.

Benchmarks

grunt bench

Here are some results on a laptop with an Intel Core i5-3210M 2.5Ghz CPU and 6Gb RAM running Ubuntu 13.10.

In the tables, fast is the normal simple-crypt Node.js code wrapper and slow is the browser code running on Node.js (not in a browser).

derive_key_from_password x10 total (ms) average (ns) diff (%)
fast 68 6,798,449 -
slow 21,970 2,197,037,726 32,217
encrypt_decrypt_asymmetric x1,000 total (ms) average (ns) diff (%)
fast 2,899 2,899,138 -
slow 131,420 131,419,631 4,433
encrypt_decrypt_symmetric x1,000 total (ms) average (ns) diff (%)
fast 422 421,697 -
slow 59,989 59,989,311 14,126
load_rsa_privkey x1,000 total (ms) average (ns) diff (%)
fast 44 44,325 -
slow 225 224,776 407
sign_verify_asymmetric x1,000 total (ms) average (ns) diff (%)
fast 2,843 2,843,213 -
slow 520,668 520,668,069 18,213
sign_verify_symmetric x1,000 total (ms) average (ns) diff (%)
fast 347 347,185 -
slow 3,130 3,129,778 801

API

Create

Key functions

Encryption

Signing

Sign-encrypt-sign

Conditional and dynamic key operations


Crypt.make([key], [options], [cb])

Create a new Crypt object which can be used to sign, verify, encrypt and decrypt data.

Parameters:

  • {String | Buffer | Object} [key] Optional key to use for operations using this object.
  • If you pass a string which looks like it's PEM-encoded then it will be loaded as a RSA key. Otherwise, strings should be binary encoded.

  • If you pass an object then its password, iterations and optional salt properties will be used to derive a key using PBKDF2-SHA1. If you don't supply a salt then a random one is created. You can also supply an optional progress property, which must be a function and is called with the percentage completion as the key is derived.

  • Omit the key (or pass undefined) if you intend to use one of the dynamic key retrieval methods.

  • {Object} [options] Optional settings:
  • {Boolean} json Whether to JSON encode and decode data. Default is true.

  • {Boolean} check Whether to add a checksum to encrypted data and verify it when decrypting data. Default is true.

  • {Boolean} pad Whether to automatically pad encrypted data (using PKCS#7) to a multiple of the AES block size (16 bytes). Default is true.

  • {Function} [cb] Optional function called with the Crypt object. It's passed the following arguments:
  • {Object} err If an error occurred then details of the error, otherwise null.

  • {Crypt} crypt The Crypt object. key (above) is parsed using parse_key and is available using get_key.

Return:

{Crypt} The Crypt object. It will have no key until key parsing is complete and cb is called.

Go: TOC | Crypt

Crypt.get_key_size()

Get the size (in bytes) of symmetric encryption keys. Use this value when creating keys for use with Crypt.prototype.encrypt and Crypt.prototype.decrypt.

Return:

{Number} Encryption key size.

Go: TOC | Crypt

Crypt.parse_key(key, cb)

Parse a key. Call this if you want to use the same key for multiple Crypt objects but only incur the cost of parsing it once.

Parameters:

  • {String | Buffer | Object} key Key to parse. See the key parameter of Crypt.make.
  • {Function} cb Function called with the parsed key. It's passed the following arguments:
  • {Object} err If an error occurred then details of the error, otherwise null.

  • {String|Buffer|Object} key Parsed key. You can pass this to Crypt.make. If the key looks like a PEM-encoded RSA key then an internal RSA key object is returned. If the key is an object (with password, iterations and optional salt properties) then an object with the following properties is returned:

    • {Object} key An AES encryption key derived using PBKDF2-SHA-1.

    • {Buffer|String} salt Binary-encoded salt value which was used to derive key.

Go: TOC | Crypt

Crypt.prototype.get_key()

Get the key being used by this Crypt object.

Return:

{Object | Buffer | String} The key. This could be a Buffer, binary-encoded string, internal RSA key object or an object containing a key derived from a password (see parse_key).

Go: TOC | Crypt.prototype

Crypt.prototype.encrypt(data, [iv], cb)

Encrypt data using AES-128-CBC and the key you passed to Crypt.make when you created this object. If you passed a (PEM-encoded) RSA public key then a random AES key is created and the public key is used to encrypt it (using RSAES-OAEP). The encrypted AES key is made available along with the encrypted data (see below).

Parameters:

  • {Object | Buffer | String} data The data to be encrypted.
  • If you didn't pass options.json as false to Crypt.make then the data will be JSON-serialized before it's encrypted. Otherwise, it must be a Buffer or binary-encoded string.

  • If you didn't pass options.check as false to Crypt.make then a SHA-256 checksum is prepended to the data before it's encrypted.

  • If you didn't pass options.pad as false to Crypt.make then the data will be padded to a multiple of 16 bytes.

  • {Buffer | String} [iv] Optional initialisation vector (salt) to use for AES encryption. If not supplied, a random one is created.
  • {Function} cb Function called with the result. It's passed the following arguments:
  • {Object} err If an error occurred then details of the error, otherwise null.
  • {Object} result Result of the encryption. Typically you would JSON serialize this for transmission. It has the following properties:

    • {String} iv Base64-encoded initialisation vector used for the encryption.
    • {String} data Base64-encoded encrypted data.

    • {String} ekey Base64-encoded encrypted AES key (only present when using RSA public key -- see above).

    • {Number} version Internal version number for future compatibility checking.

Go: TOC | Crypt.prototype

Crypt.prototype.decrypt(data, cb)

Decrypt data using AES-128-CBC and the key you passed to Crypt.make when you created this object. If you passed a (PEM-encoded) RSA private key then an ekey property is expected to be present on the data parameter (below). The private key is used to decrypt the ekey in order to obtain the AES key.

Parameters:

  • {Object} data A result object returned by encrypt. You may have received this from another party, for instance.
  • If you didn't pass options.json as false to Crypt.make then the data will be JSON-parsed after it's encrypted. Otherwise, you'll receive a Buffer (on Node.js) or binary-encoded string.

  • If you didn't pass options.check as false to Crypt.make then a SHA-256 checksum is expected to be prepended to the decrypted data. The checksum is verified against the rest of the decrypted data.

  • If you didn't pass options.pad as false to Crypt.make then the decrypted data is expected to be padded to a multiple of 16 bytes and will be unpadded automatically.

  • {Function} cb Function called with the result. It's passed the following arguments:
  • {Object} err If an error occurred then details of the error, otherwise null.

  • {Object|Buffer|String} data The decrypted data.

Go: TOC | Crypt.prototype

Crypt.prototype.sign(data, cb)

Sign a SHA-256 hash of some data using the key you passed to Crypt.make when you created this object. If you passed a (PEM-encoded) RSA private key then the hash is signed using RSASSA-PSS. Otherwise, HMAC-SHA-256 is used to sign the data.

Parameters:

  • {Object | Buffer | String} data The data to be signed.
  • If you didn't pass options.json as false to Crypt.make then the data will be JSON-serialized before it's encrypted. Otherwise, it must be a Buffer or binary-encoded string.
  • {Function} cb Function called with the result. It's passed the following arguments:
  • {Object} err If an error occurred then details of the error, otherwise null.
  • {Object} result Result of signing the data. Typically you would JSON serialize this for transmission. It has the following properties:

    • {String} data The data that was signed (Base64-encoded).

    • {String} signature Base64-encoded signed hash of the data.

    • {Number} version Internal version number for future compatibility checking.

Go: TOC | Crypt.prototype

Crypt.prototype.verify(data, cb)

Verify a signature by comparing it to a signed SHA-256 hash of some data. The signed hash is generated using the key you passed to Crypt.make when you created this object. If you passed a (PEM-encoded) RSA public key then the hash is signed using RSASSA-PSS. Otherwise HMAC is used.

Parameters:

  • {Object} data A result object returned by sign. You may have received this from another party, for instance.
  • If you didn't pass options.json as false to Crypt.make then the data will be JSON-parsed after it's verified. Otherwise, you'll receive a Buffer (on Node.js) or binary-encoded string.
  • {Function} cb Function called with the result. It's passed the following arguments:
  • {Object} err If an error occurred then details of the error, otherwise null.

  • {Object|Buffer|String} data The verified data.

Go: TOC | Crypt.prototype

Crypt.sign_encrypt_sign(signing_key, encryption_key, data, [iv], cb)

Sign then encrypt then sign data. Convenience function which creates two Crypt objects, calls sign on the first, plumbs the result into encrypt on the second and then plumbs the result from that into sign on the first again. See this article for a discussion of why just sign then encrypt isn't good enough.

Parameters:

  • {String} signing_key Key to use for signing the data.
  • {String} encryption_key Key to use for encrypting the data and signature.
  • {Object | Buffer | String} data The data to be signed and encrypted.
  • {Buffer | String} [iv] Optional initialisation vector (salt) to use for encryption. If not supplied, a random one is created.
  • {Function} cb Function called with the result. It's passed the following arguments:
  • {Object} err If an error occurred then details of the error, otherwise null.
  • {Object} result Result of signing and encrypting the data. See the description of cb for sign.

Go: TOC | Crypt

Crypt.verify_decrypt_verify(decryption_key, verifying_key, data, cb)

Verify then decrypt then verify data. Convenience function which creates two Crypt objects, calls verify on the first, plumbs the result into decrypt on the second and then plumbs the result from that into verify on the first again.

Parameters:

  • {String} decryption_key Key to use for decrypting the data and signature.
  • {String} verifying_key Key to use for verifying the signature.
  • {Function} cb Function called with the result. It's passed the following arguments:
  • {Object} err If an error occurred then details of the error, otherwise null.

  • {Object|Buffer|String} data The decrypted and verified data.

Go: TOC | Crypt

Crypt.prototype.maybe_encrypt(encrypt, data, cb, [get_key])

Conditionally encrypt data using encrypt.

Parameters:

  • {Boolean} encrypt Whether to encrypt the data.
  • {Object | Buffer | String} data The data to encrypt.
  • {Function} cb Function called with the result. It's passed the following arguments:
  • {Object} err If an error occurred then details of the error, otherwise null.

  • {Object} result Result object with the following properties:

    • {Boolean} encrypted Whether the data was encrypted.

    • {Object} data Encryption result (data, initialisation vector etc) if the data was encrypted, otherwise the data.

    • {Object} [key_data] If the data was encrypted and get_key was called (see below) then this is the key data returned by get_key.

  • {Function} [get_key] Optional function to call in order to get the encryption key. You must supply this if you didn't supply a key when creating the Crypt object. get_key is called with the following arguments:
  • The arguments to maybe_encrypt that follow get_key (if any).

  • {Function} got_key Function to call with the key. You should call it with the following arguments:

    • {Object} err If an error occurred then details of the error, otherwise null.

    • {Object|Buffer|String} key The encryption key. If this is a falsey value then the data won't be encrypted.

    • {Object} [key_data] Optional metadata for the key. This is included in the result (see above).

    • {Buffer|String} [iv] Optional initialisation vector.

Go: TOC | Crypt.prototype

Crypt.prototype.maybe_decrypt(data, cb, [get_key])

Conditionally decrypt data using decrypt.

Parameters:

  • {Function} cb Function called with the result. It's passed the following arguments:
  • {Object} err If an Error occurred then details of the error, otherwise null.

  • {Object|Buffer|String} data If the data was encrypted then the decrypted data otherwise the data.

  • {Function} [get_key] Optional function to call in order to get the encryption key. You must supply this if you didn't supply a key when creating the Crypt object. get_key is called with the following arguments:
  • The arguments to maybe_decrypt that follow get_key (if any).

  • {Function} got_key Function to call with the key. You should call it with the following arguments:

    • {Object} err If an error occurred then details of the error, otherwise null.

    • {Object|Buffer|String} key The decryption key.

  • {Object} [key_data] Metadata for the key which was supplied in maybe_encrypt (if any).

Go: TOC | Crypt.prototype

Crypt.prototype.maybe_sign(sign, data, cb, [get_key])

Conditionally sign data using sign.

Parameters:

  • {Boolean} sign Whether to sign the data.
  • {Object | Buffer | String} data The data to sign.
  • {Function} cb Function called with the result. It's passed the following arguments:
  • {Object} err If an error occurred then details of the error, otherwise null.

  • {Object} result Result object with the following properties:

    • {Boolean} signed Whether the data was signed.

    • {Object} data Signing result (data, signature etc) if the data was signed, otherwise the data.

    • {Object} [key_data] If the data was signed and get_key was called (see below) then this is the key data returned by get_key.

  • {Function} [get_key] Optional function to call in order to get the signing key. You must supply this if you didn't supply a key when creating the Crypt object. get_key is called with the following arguments:
  • The arguments to maybe_sign that follow get_key (if any).

  • {Function} got_key Function to call with the key. You should call it with the following arguments:

    • {Object} err If an error occurred then details of the error, otherwise null.

    • {Object|Buffer|String} key The signing key. If this is a falsey value then the data won't be signed.

    • {Object} [key_data] Optional metadata for the key. This is included in the result (see above).

Go: TOC | Crypt.prototype

Crypt.prototype.maybe_verify(data, cb, [get_key])

Conditionally verify data using verify.

Parameters:

  • {Object} data A result object returned by maybe_sign.
  • {Function} cb Function called with the result. It's passed the following arguments:
  • {Object} err If an Error occurred then details of the error, otherwise null.

  • {Object|Buffer|String} data If the data was signed then the verified data otherwise the data.

  • {Function} [get_key] Optional function to call in order to get the verifying key. You must supply this if you didn't supply a key when creating the Crypt object. get_key is called with the following arguments:
  • The arguments to maybe_verify that follow get_key (if any).

  • {Function} got_key Function to call with the key. You should call it with the following arguments:

    • {Object} err If an error occurred then details of the error, otherwise null.

    • {Object|Buffer|String} key The verifying key.

  • {Object} [key_data] Metadata for the key which was supplied in maybe_sign (if any).

Go: TOC | Crypt.prototype

—generated by apidox

npm loves you