node/lib/crypto.js

681 lines
18 KiB
JavaScript
Raw Normal View History

2011-03-10 08:54:52 +00:00
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
// Note: In 0.8 and before, crypto functions all defaulted to using
// binary-encoded strings rather than buffers.
exports.DEFAULT_ENCODING = 'buffer';
try {
var binding = process.binding('crypto');
var SecureContext = binding.SecureContext;
var randomBytes = binding.randomBytes;
var pseudoRandomBytes = binding.pseudoRandomBytes;
var getCiphers = binding.getCiphers;
2012-10-13 00:44:11 +00:00
var getHashes = binding.getHashes;
} catch (e) {
throw new Error('node.js not compiled with openssl crypto support.');
}
var constants = require('constants');
2012-10-29 18:31:59 +00:00
var stream = require('stream');
var util = require('util');
// This is here because many functions accepted binary strings without
// any explicit encoding in older versions of node, and we don't want
// to break them unnecessarily.
function toBuf(str, encoding) {
encoding = encoding || 'binary';
if (util.isString(str)) {
if (encoding === 'buffer')
encoding = 'binary';
str = new Buffer(str, encoding);
}
return str;
}
var assert = require('assert');
var StringDecoder = require('string_decoder').StringDecoder;
function Credentials(secureProtocol, flags, context) {
2010-11-30 23:59:59 +00:00
if (!(this instanceof Credentials)) {
2013-03-29 16:39:51 +00:00
return new Credentials(secureProtocol, flags, context);
2010-11-30 23:59:59 +00:00
}
if (context) {
this.context = context;
2010-11-29 07:20:59 +00:00
} else {
this.context = new SecureContext();
2010-11-29 07:20:59 +00:00
if (secureProtocol) {
this.context.init(secureProtocol);
} else {
this.context.init();
}
}
if (flags) this.context.setOptions(flags);
}
2010-11-30 23:59:59 +00:00
exports.Credentials = Credentials;
function addNewline(buf) {
var last = buf[buf.length - 1];
var isBuf = Buffer.isBuffer(buf);
if (!isBuf && !util.isString(buf))
throw new Error('Certificate should be of type Buffer or string');
if (isBuf ? last !== 10 : last !== '\n')
return buf.toString().trim() + '\n';
else
return buf;
}
2010-11-30 23:59:59 +00:00
exports.createCredentials = function(options, context) {
2010-11-30 23:59:59 +00:00
if (!options) options = {};
var c = new Credentials(options.secureProtocol,
options.secureOptions,
context);
if (context) return c;
2010-11-29 07:20:59 +00:00
if (options.key) {
var key = addNewline(options.key);
if (options.passphrase) {
c.context.setKey(key, options.passphrase);
} else {
c.context.setKey(key);
}
}
2010-11-30 23:59:59 +00:00
if (options.cert) c.context.setCert(addNewline(options.cert));
2010-11-30 23:59:59 +00:00
if (options.ciphers) c.context.setCiphers(options.ciphers);
if (options.ecdhCurve) c.context.setECDHCurve(options.ecdhCurve);
2010-11-30 23:59:59 +00:00
if (options.ca) {
if (util.isArray(options.ca)) {
2010-11-30 23:59:59 +00:00
for (var i = 0, len = options.ca.length; i < len; i++) {
c.context.addCACert(options.ca[i]);
}
} else {
2010-11-30 23:59:59 +00:00
c.context.addCACert(options.ca);
}
} else {
2010-12-01 00:13:05 +00:00
c.context.addRootCerts();
}
2010-11-30 23:59:59 +00:00
2010-11-23 18:00:42 +00:00
if (options.crl) {
if (util.isArray(options.crl)) {
for (var i = 0, len = options.crl.length; i < len; i++) {
2010-11-23 18:00:42 +00:00
c.context.addCRL(options.crl[i]);
}
} else {
c.context.addCRL(options.crl);
}
}
if (options.sessionIdContext) {
c.context.setSessionIdContext(options.sessionIdContext);
}
if (options.pfx) {
2012-10-13 00:36:18 +00:00
var pfx = options.pfx;
var passphrase = options.passphrase;
pfx = toBuf(pfx);
if (passphrase)
passphrase = toBuf(passphrase);
2012-10-13 00:36:18 +00:00
if (passphrase) {
c.context.loadPKCS12(pfx, passphrase);
} else {
2012-10-13 00:36:18 +00:00
c.context.loadPKCS12(pfx);
}
}
return c;
2010-10-07 03:05:23 +00:00
};
2010-11-29 07:20:59 +00:00
2013-03-15 08:59:30 +00:00
function LazyTransform(options) {
this._options = options;
}
util.inherits(LazyTransform, stream.Transform);
[
'_readableState',
'_writableState',
'_transformState'
].forEach(function(prop, i, props) {
Object.defineProperty(LazyTransform.prototype, prop, {
get: function() {
stream.Transform.call(this, this._options);
this._writableState.decodeStrings = false;
this._writableState.defaultEncoding = 'binary';
return this[prop];
},
set: function(val) {
Object.defineProperty(this, prop, {
value: val,
enumerable: true,
configurable: true,
writable: true
});
},
configurable: true,
enumerable: true
});
2013-03-15 08:59:30 +00:00
});
exports.createHash = exports.Hash = Hash;
2012-10-29 18:31:59 +00:00
function Hash(algorithm, options) {
if (!(this instanceof Hash))
2013-03-29 16:39:51 +00:00
return new Hash(algorithm, options);
this._binding = new binding.Hash(algorithm);
2013-03-15 08:59:30 +00:00
LazyTransform.call(this, options);
}
2013-03-15 08:59:30 +00:00
util.inherits(Hash, LazyTransform);
2012-10-29 18:31:59 +00:00
Hash.prototype._transform = function(chunk, encoding, callback) {
this._binding.update(chunk, encoding);
2012-10-29 18:31:59 +00:00
callback();
};
Hash.prototype._flush = function(callback) {
2013-05-02 22:32:32 +00:00
var encoding = this._readableState.encoding || 'buffer';
this.push(this._binding.digest(encoding), encoding);
2012-10-29 18:31:59 +00:00
callback();
};
Hash.prototype.update = function(data, encoding) {
encoding = encoding || exports.DEFAULT_ENCODING;
if (encoding === 'buffer' && util.isString(data))
encoding = 'binary';
this._binding.update(data, encoding);
return this;
};
Hash.prototype.digest = function(outputEncoding) {
outputEncoding = outputEncoding || exports.DEFAULT_ENCODING;
2013-05-03 01:38:04 +00:00
return this._binding.digest(outputEncoding);
2010-10-07 03:05:23 +00:00
};
2010-11-29 07:20:59 +00:00
exports.createHmac = exports.Hmac = Hmac;
2012-10-29 22:21:25 +00:00
function Hmac(hmac, key, options) {
if (!(this instanceof Hmac))
2013-03-29 16:39:51 +00:00
return new Hmac(hmac, key, options);
this._binding = new binding.Hmac();
this._binding.init(hmac, toBuf(key));
2013-03-15 08:59:30 +00:00
LazyTransform.call(this, options);
2012-10-13 00:36:18 +00:00
}
2010-05-03 22:37:49 +00:00
2013-03-15 08:59:30 +00:00
util.inherits(Hmac, LazyTransform);
Hmac.prototype.update = Hash.prototype.update;
Hmac.prototype.digest = Hash.prototype.digest;
2012-10-29 22:21:25 +00:00
Hmac.prototype._flush = Hash.prototype._flush;
Hmac.prototype._transform = Hash.prototype._transform;
function getDecoder(decoder, encoding) {
if (encoding === 'utf-8') encoding = 'utf8'; // Normalize encoding.
decoder = decoder || new StringDecoder(encoding);
assert(decoder.encoding === encoding, 'Cannot change encoding');
return decoder;
}
2010-11-29 07:20:59 +00:00
exports.createCipher = exports.Cipher = Cipher;
function Cipher(cipher, password, options) {
if (!(this instanceof Cipher))
2013-03-29 16:39:51 +00:00
return new Cipher(cipher, password, options);
2013-03-12 10:09:27 +00:00
this._binding = new binding.CipherBase(true);
2012-10-13 00:36:18 +00:00
this._binding.init(cipher, toBuf(password));
this._decoder = null;
2013-03-15 08:59:30 +00:00
LazyTransform.call(this, options);
2012-10-13 00:36:18 +00:00
}
2010-05-03 22:37:49 +00:00
2013-03-15 08:59:30 +00:00
util.inherits(Cipher, LazyTransform);
Cipher.prototype._transform = function(chunk, encoding, callback) {
this.push(this._binding.update(chunk, encoding));
callback();
};
Cipher.prototype._flush = function(callback) {
try {
this.push(this._binding.final());
} catch (e) {
callback(e);
return;
}
callback();
};
Cipher.prototype.update = function(data, inputEncoding, outputEncoding) {
inputEncoding = inputEncoding || exports.DEFAULT_ENCODING;
outputEncoding = outputEncoding || exports.DEFAULT_ENCODING;
var ret = this._binding.update(data, inputEncoding);
if (outputEncoding && outputEncoding !== 'buffer') {
this._decoder = getDecoder(this._decoder, outputEncoding);
ret = this._decoder.write(ret);
}
2010-11-29 07:20:59 +00:00
return ret;
2010-10-07 03:05:23 +00:00
};
2010-05-03 22:37:49 +00:00
Cipher.prototype.final = function(outputEncoding) {
outputEncoding = outputEncoding || exports.DEFAULT_ENCODING;
var ret = this._binding.final();
if (outputEncoding && outputEncoding !== 'buffer') {
this._decoder = getDecoder(this._decoder, outputEncoding);
ret = this._decoder.end(ret);
}
return ret;
};
2010-11-29 07:20:59 +00:00
Cipher.prototype.setAutoPadding = function(ap) {
this._binding.setAutoPadding(ap);
return this;
2010-10-07 03:05:23 +00:00
};
2010-05-03 22:37:49 +00:00
2010-11-29 07:20:59 +00:00
exports.createCipheriv = exports.Cipheriv = Cipheriv;
function Cipheriv(cipher, key, iv, options) {
if (!(this instanceof Cipheriv))
2013-03-29 16:39:51 +00:00
return new Cipheriv(cipher, key, iv, options);
2013-03-12 10:09:27 +00:00
this._binding = new binding.CipherBase(true);
this._binding.initiv(cipher, toBuf(key), toBuf(iv));
this._decoder = null;
2013-03-15 08:59:30 +00:00
LazyTransform.call(this, options);
}
2013-03-15 08:59:30 +00:00
util.inherits(Cipheriv, LazyTransform);
Cipheriv.prototype._transform = Cipher.prototype._transform;
Cipheriv.prototype._flush = Cipher.prototype._flush;
Cipheriv.prototype.update = Cipher.prototype.update;
Cipheriv.prototype.final = Cipher.prototype.final;
Cipheriv.prototype.setAutoPadding = Cipher.prototype.setAutoPadding;
Cipheriv.prototype.getAuthTag = function() {
return this._binding.getAuthTag();
};
Cipheriv.prototype.setAuthTag = function(tagbuf) {
this._binding.setAuthTag(tagbuf);
};
exports.createDecipher = exports.Decipher = Decipher;
function Decipher(cipher, password, options) {
if (!(this instanceof Decipher))
2013-03-29 16:39:51 +00:00
return new Decipher(cipher, password, options);
2012-10-13 00:36:18 +00:00
2013-03-12 10:09:27 +00:00
this._binding = new binding.CipherBase(false);
this._binding.init(cipher, toBuf(password));
this._decoder = null;
2013-03-15 08:59:30 +00:00
LazyTransform.call(this, options);
2012-10-13 00:36:18 +00:00
}
2010-05-03 22:37:49 +00:00
2013-03-15 08:59:30 +00:00
util.inherits(Decipher, LazyTransform);
Decipher.prototype._transform = Cipher.prototype._transform;
Decipher.prototype._flush = Cipher.prototype._flush;
Decipher.prototype.update = Cipher.prototype.update;
Decipher.prototype.final = Cipher.prototype.final;
Decipher.prototype.finaltol = Cipher.prototype.final;
Decipher.prototype.setAutoPadding = Cipher.prototype.setAutoPadding;
2010-11-29 07:20:59 +00:00
exports.createDecipheriv = exports.Decipheriv = Decipheriv;
function Decipheriv(cipher, key, iv, options) {
if (!(this instanceof Decipheriv))
2013-03-29 16:39:51 +00:00
return new Decipheriv(cipher, key, iv, options);
2013-03-12 10:09:27 +00:00
this._binding = new binding.CipherBase(false);
this._binding.initiv(cipher, toBuf(key), toBuf(iv));
this._decoder = null;
2013-03-15 08:59:30 +00:00
LazyTransform.call(this, options);
2012-10-13 00:36:18 +00:00
}
2013-03-15 08:59:30 +00:00
util.inherits(Decipheriv, LazyTransform);
Decipheriv.prototype._transform = Cipher.prototype._transform;
Decipheriv.prototype._flush = Cipher.prototype._flush;
Decipheriv.prototype.update = Cipher.prototype.update;
Decipheriv.prototype.final = Cipher.prototype.final;
Decipheriv.prototype.finaltol = Cipher.prototype.final;
Decipheriv.prototype.setAutoPadding = Cipher.prototype.setAutoPadding;
Decipheriv.prototype.getAuthTag = Cipheriv.prototype.getAuthTag;
Decipheriv.prototype.setAuthTag = Cipheriv.prototype.setAuthTag;
exports.createSign = exports.Sign = Sign;
function Sign(algorithm, options) {
if (!(this instanceof Sign))
2013-03-29 16:39:51 +00:00
return new Sign(algorithm, options);
this._binding = new binding.Sign();
this._binding.init(algorithm);
stream.Writable.call(this, options);
2012-10-13 00:36:18 +00:00
}
util.inherits(Sign, stream.Writable);
Sign.prototype._write = function(chunk, encoding, callback) {
this._binding.update(chunk, encoding);
callback();
};
Sign.prototype.update = Hash.prototype.update;
2012-10-13 00:36:18 +00:00
Sign.prototype.sign = function(options, encoding) {
if (!options)
throw new Error('No key provided to sign');
var key = options.key || options;
var passphrase = options.passphrase || null;
var ret = this._binding.sign(toBuf(key), null, passphrase);
encoding = encoding || exports.DEFAULT_ENCODING;
if (encoding && encoding !== 'buffer')
ret = ret.toString(encoding);
return ret;
};
exports.createVerify = exports.Verify = Verify;
function Verify(algorithm, options) {
if (!(this instanceof Verify))
2013-03-29 16:39:51 +00:00
return new Verify(algorithm, options);
this._binding = new binding.Verify;
this._binding.init(algorithm);
stream.Writable.call(this, options);
}
util.inherits(Verify, stream.Writable);
Verify.prototype._write = Sign.prototype._write;
Verify.prototype.update = Sign.prototype.update;
2012-10-13 00:36:18 +00:00
Verify.prototype.verify = function(object, signature, sigEncoding) {
sigEncoding = sigEncoding || exports.DEFAULT_ENCODING;
return this._binding.verify(toBuf(object), toBuf(signature, sigEncoding));
};
exports.createDiffieHellman = exports.DiffieHellman = DiffieHellman;
function DiffieHellman(sizeOrKey, encoding) {
if (!(this instanceof DiffieHellman))
return new DiffieHellman(sizeOrKey, encoding);
if (!sizeOrKey)
this._binding = new binding.DiffieHellman();
else {
encoding = encoding || exports.DEFAULT_ENCODING;
sizeOrKey = toBuf(sizeOrKey, encoding);
this._binding = new binding.DiffieHellman(sizeOrKey);
}
}
exports.DiffieHellmanGroup =
exports.createDiffieHellmanGroup =
exports.getDiffieHellman = DiffieHellmanGroup;
function DiffieHellmanGroup(name) {
if (!(this instanceof DiffieHellmanGroup))
return new DiffieHellmanGroup(name);
this._binding = new binding.DiffieHellmanGroup(name);
}
DiffieHellmanGroup.prototype.generateKeys =
DiffieHellman.prototype.generateKeys =
dhGenerateKeys;
function dhGenerateKeys(encoding) {
var keys = this._binding.generateKeys();
encoding = encoding || exports.DEFAULT_ENCODING;
if (encoding && encoding !== 'buffer')
keys = keys.toString(encoding);
return keys;
}
DiffieHellmanGroup.prototype.computeSecret =
DiffieHellman.prototype.computeSecret =
dhComputeSecret;
function dhComputeSecret(key, inEnc, outEnc) {
inEnc = inEnc || exports.DEFAULT_ENCODING;
outEnc = outEnc || exports.DEFAULT_ENCODING;
var ret = this._binding.computeSecret(toBuf(key, inEnc));
if (outEnc && outEnc !== 'buffer')
ret = ret.toString(outEnc);
return ret;
}
DiffieHellmanGroup.prototype.getPrime =
DiffieHellman.prototype.getPrime =
dhGetPrime;
function dhGetPrime(encoding) {
var prime = this._binding.getPrime();
encoding = encoding || exports.DEFAULT_ENCODING;
if (encoding && encoding !== 'buffer')
prime = prime.toString(encoding);
return prime;
}
DiffieHellmanGroup.prototype.getGenerator =
DiffieHellman.prototype.getGenerator =
dhGetGenerator;
function dhGetGenerator(encoding) {
var generator = this._binding.getGenerator();
encoding = encoding || exports.DEFAULT_ENCODING;
if (encoding && encoding !== 'buffer')
generator = generator.toString(encoding);
return generator;
}
DiffieHellmanGroup.prototype.getPublicKey =
DiffieHellman.prototype.getPublicKey =
dhGetPublicKey;
function dhGetPublicKey(encoding) {
var key = this._binding.getPublicKey();
encoding = encoding || exports.DEFAULT_ENCODING;
if (encoding && encoding !== 'buffer')
key = key.toString(encoding);
return key;
}
DiffieHellmanGroup.prototype.getPrivateKey =
DiffieHellman.prototype.getPrivateKey =
dhGetPrivateKey;
function dhGetPrivateKey(encoding) {
var key = this._binding.getPrivateKey();
encoding = encoding || exports.DEFAULT_ENCODING;
if (encoding && encoding !== 'buffer')
key = key.toString(encoding);
return key;
}
2011-08-11 09:40:55 +00:00
DiffieHellman.prototype.setPublicKey = function(key, encoding) {
encoding = encoding || exports.DEFAULT_ENCODING;
this._binding.setPublicKey(toBuf(key, encoding));
return this;
};
DiffieHellman.prototype.setPrivateKey = function(key, encoding) {
encoding = encoding || exports.DEFAULT_ENCODING;
this._binding.setPrivateKey(toBuf(key, encoding));
return this;
};
2012-10-13 00:36:18 +00:00
exports.pbkdf2 = function(password, salt, iterations, keylen, callback) {
if (!util.isFunction(callback))
throw new Error('No callback provided to pbkdf2');
return pbkdf2(password, salt, iterations, keylen, callback);
};
exports.pbkdf2Sync = function(password, salt, iterations, keylen) {
return pbkdf2(password, salt, iterations, keylen);
};
function pbkdf2(password, salt, iterations, keylen, callback) {
password = toBuf(password);
salt = toBuf(salt);
if (exports.DEFAULT_ENCODING === 'buffer')
return binding.PBKDF2(password, salt, iterations, keylen, callback);
// at this point, we need to handle encodings.
var encoding = exports.DEFAULT_ENCODING;
if (callback) {
binding.PBKDF2(password, salt, iterations, keylen, function(er, ret) {
if (ret)
ret = ret.toString(encoding);
callback(er, ret);
});
} else {
var ret = binding.PBKDF2(password, salt, iterations, keylen);
return ret.toString(encoding);
}
}
2012-10-13 00:36:18 +00:00
exports.Certificate = Certificate;
function Certificate() {
if (!(this instanceof Certificate))
return new Certificate();
this._binding = new binding.Certificate();
}
Certificate.prototype.verifySpkac = function(object) {
return this._binding.verifySpkac(object);
};
Certificate.prototype.exportPublicKey = function(object, encoding) {
return this._binding.exportPublicKey(toBuf(object, encoding));
};
Certificate.prototype.exportChallenge = function(object, encoding) {
return this._binding.exportChallenge(toBuf(object, encoding));
};
exports.setEngine = function setEngine(id, flags) {
if (!util.isString(id))
throw new TypeError('id should be a string');
if (flags && !util.isNumber(flags))
throw new TypeError('flags should be a number, if present');
flags = flags >>> 0;
// Use provided engine for everything by default
if (flags === 0)
flags = constants.ENGINE_METHOD_ALL;
return binding.setEngine(id, flags);
};
exports.randomBytes = randomBytes;
exports.pseudoRandomBytes = pseudoRandomBytes;
exports.rng = randomBytes;
exports.prng = pseudoRandomBytes;
exports.getCiphers = function() {
return filterDuplicates(getCiphers.call(null, arguments));
};
2012-10-13 00:44:11 +00:00
exports.getHashes = function() {
return filterDuplicates(getHashes.call(null, arguments));
2012-10-13 00:44:11 +00:00
};
function filterDuplicates(names) {
2012-10-13 00:44:11 +00:00
// Drop all-caps names in favor of their lowercase aliases,
// for example, 'sha1' instead of 'SHA1'.
var ctx = {};
names.forEach(function(name) {
2012-10-13 00:44:11 +00:00
if (/^[0-9A-Z\-]+$/.test(name)) name = name.toLowerCase();
ctx[name] = true;
});
return Object.getOwnPropertyNames(ctx).sort();
}