--- /dev/null
- var read = this._buffer.use(this.pair.ssl, out, size);
+// 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.
+
+var assert = require('assert');
+var crypto = require('crypto');
+var events = require('events');
+var stream = require('stream');
+var tls = require('tls');
+var util = require('util');
+
+var Timer = process.binding('timer_wrap').Timer;
+var Connection = null;
+try {
+ Connection = process.binding('crypto').Connection;
+} catch (e) {
+ throw new Error('node.js not compiled with openssl crypto support.');
+}
+
+var debug = util.debuglog('tls-legacy');
+
+function SlabBuffer() {
+ this.create();
+}
+
+
+SlabBuffer.prototype.create = function create() {
+ this.isFull = false;
+ this.pool = new Buffer(tls.SLAB_BUFFER_SIZE);
+ this.offset = 0;
+ this.remaining = this.pool.length;
+};
+
+
+SlabBuffer.prototype.use = function use(context, fn, size) {
+ if (this.remaining === 0) {
+ this.isFull = true;
+ return 0;
+ }
+
+ var actualSize = this.remaining;
+
+ if (!util.isNull(size)) actualSize = Math.min(size, actualSize);
+
+ var bytes = fn.call(context, this.pool, this.offset, actualSize);
+ if (bytes > 0) {
+ this.offset += bytes;
+ this.remaining -= bytes;
+ }
+
+ assert(this.remaining >= 0);
+
+ return bytes;
+};
+
+
+var slabBuffer = null;
+
+
+// Base class of both CleartextStream and EncryptedStream
+function CryptoStream(pair, options) {
+ stream.Duplex.call(this, options);
+
+ this.pair = pair;
+ this._pending = null;
+ this._pendingEncoding = '';
+ this._pendingCallback = null;
+ this._doneFlag = false;
+ this._retryAfterPartial = false;
+ this._halfRead = false;
+ this._sslOutCb = null;
+ this._resumingSession = false;
+ this._reading = true;
+ this._destroyed = false;
+ this._ended = false;
+ this._finished = false;
+ this._opposite = null;
+
+ if (util.isNull(slabBuffer)) slabBuffer = new SlabBuffer();
+ this._buffer = slabBuffer;
+
+ this.once('finish', onCryptoStreamFinish);
+
+ // net.Socket calls .onend too
+ this.once('end', onCryptoStreamEnd);
+}
+util.inherits(CryptoStream, stream.Duplex);
+
+
+function onCryptoStreamFinish() {
+ this._finished = true;
+
+ if (this === this.pair.cleartext) {
+ debug('cleartext.onfinish');
+ if (this.pair.ssl) {
+ // Generate close notify
+ // NOTE: first call checks if client has sent us shutdown,
+ // second call enqueues shutdown into the BIO.
+ if (this.pair.ssl.shutdown() !== 1) {
+ if (this.pair.ssl && this.pair.ssl.error)
+ return this.pair.error();
+
+ this.pair.ssl.shutdown();
+ }
+
+ if (this.pair.ssl && this.pair.ssl.error)
+ return this.pair.error();
+ }
+ } else {
+ debug('encrypted.onfinish');
+ }
+
+ // Try to read just to get sure that we won't miss EOF
+ if (this._opposite.readable) this._opposite.read(0);
+
+ if (this._opposite._ended) {
+ this._done();
+
+ // No half-close, sorry
+ if (this === this.pair.cleartext) this._opposite._done();
+ }
+}
+
+
+function onCryptoStreamEnd() {
+ this._ended = true;
+ if (this === this.pair.cleartext) {
+ debug('cleartext.onend');
+ } else {
+ debug('encrypted.onend');
+ }
+}
+
+
+// NOTE: Called once `this._opposite` is set.
+CryptoStream.prototype.init = function init() {
+ var self = this;
+ this._opposite.on('sslOutEnd', function() {
+ if (self._sslOutCb) {
+ var cb = self._sslOutCb;
+ self._sslOutCb = null;
+ cb(null);
+ }
+ });
+};
+
+
+CryptoStream.prototype._write = function write(data, encoding, cb) {
+ assert(util.isNull(this._pending));
+
+ // Black-hole data
+ if (!this.pair.ssl) return cb(null);
+
+ // When resuming session don't accept any new data.
+ // And do not put too much data into openssl, before writing it from encrypted
+ // side.
+ //
+ // TODO(indutny): Remove magic number, use watermark based limits
+ if (!this._resumingSession &&
+ this._opposite._internallyPendingBytes() < 128 * 1024) {
+ // Write current buffer now
+ var written;
+ if (this === this.pair.cleartext) {
+ debug('cleartext.write called with %d bytes', data.length);
+ written = this.pair.ssl.clearIn(data, 0, data.length);
+ } else {
+ debug('encrypted.write called with %d bytes', data.length);
+ written = this.pair.ssl.encIn(data, 0, data.length);
+ }
+
+ // Handle and report errors
+ if (this.pair.ssl && this.pair.ssl.error) {
+ return cb(this.pair.error(true));
+ }
+
+ // Force SSL_read call to cycle some states/data inside OpenSSL
+ this.pair.cleartext.read(0);
+
+ // Cycle encrypted data
+ if (this.pair.encrypted._internallyPendingBytes())
+ this.pair.encrypted.read(0);
+
+ // Get NPN and Server name when ready
+ this.pair.maybeInitFinished();
+
+ // Whole buffer was written
+ if (written === data.length) {
+ if (this === this.pair.cleartext) {
+ debug('cleartext.write succeed with ' + written + ' bytes');
+ } else {
+ debug('encrypted.write succeed with ' + written + ' bytes');
+ }
+
+ // Invoke callback only when all data read from opposite stream
+ if (this._opposite._halfRead) {
+ assert(util.isNull(this._sslOutCb));
+ this._sslOutCb = cb;
+ } else {
+ cb(null);
+ }
+ return;
+ } else if (written !== 0 && written !== -1) {
+ assert(!this._retryAfterPartial);
+ this._retryAfterPartial = true;
+ this._write(data.slice(written), encoding, cb);
+ this._retryAfterPartial = false;
+ return;
+ }
+ } else {
+ debug('cleartext.write queue is full');
+
+ // Force SSL_read call to cycle some states/data inside OpenSSL
+ this.pair.cleartext.read(0);
+ }
+
+ // No write has happened
+ this._pending = data;
+ this._pendingEncoding = encoding;
+ this._pendingCallback = cb;
+
+ if (this === this.pair.cleartext) {
+ debug('cleartext.write queued with %d bytes', data.length);
+ } else {
+ debug('encrypted.write queued with %d bytes', data.length);
+ }
+};
+
+
+CryptoStream.prototype._writePending = function writePending() {
+ var data = this._pending,
+ encoding = this._pendingEncoding,
+ cb = this._pendingCallback;
+
+ this._pending = null;
+ this._pendingEncoding = '';
+ this._pendingCallback = null;
+ this._write(data, encoding, cb);
+};
+
+
+CryptoStream.prototype._read = function read(size) {
+ // XXX: EOF?!
+ if (!this.pair.ssl) return this.push(null);
+
+ // Wait for session to be resumed
+ // Mark that we're done reading, but don't provide data or EOF
+ if (this._resumingSession || !this._reading) return this.push('');
+
+ var out;
+ if (this === this.pair.cleartext) {
+ debug('cleartext.read called with %d bytes', size);
+ out = this.pair.ssl.clearOut;
+ } else {
+ debug('encrypted.read called with %d bytes', size);
+ out = this.pair.ssl.encOut;
+ }
+
+ var bytesRead = 0,
+ start = this._buffer.offset;
+ do {
- size -= read;
++ var read = this._buffer.use(this.pair.ssl, out, size - bytesRead);
+ if (read > 0) {
+ bytesRead += read;
- socket.destroy();
+ }
+
+ // Handle and report errors
+ if (this.pair.ssl && this.pair.ssl.error) {
+ this.pair.error();
+ break;
+ }
+
+ // Get NPN and Server name when ready
+ this.pair.maybeInitFinished();
+
+ // `maybeInitFinished()` can emit the 'secure' event which
+ // in turn destroys the connection in case of authentication
+ // failure and sets `this.pair.ssl` to `null`.
+ } while (read > 0 &&
+ !this._buffer.isFull &&
+ bytesRead < size &&
+ this.pair.ssl !== null);
+
+ // Create new buffer if previous was filled up
+ var pool = this._buffer.pool;
+ if (this._buffer.isFull) this._buffer.create();
+
+ assert(bytesRead >= 0);
+
+ if (this === this.pair.cleartext) {
+ debug('cleartext.read succeed with %d bytes', bytesRead);
+ } else {
+ debug('encrypted.read succeed with %d bytes', bytesRead);
+ }
+
+ // Try writing pending data
+ if (!util.isNull(this._pending)) this._writePending();
+ if (!util.isNull(this._opposite._pending)) this._opposite._writePending();
+
+ if (bytesRead === 0) {
+ // EOF when cleartext has finished and we have nothing to read
+ if (this._opposite._finished && this._internallyPendingBytes() === 0) {
+ // Perform graceful shutdown
+ this._done();
+
+ // No half-open, sorry!
+ if (this === this.pair.cleartext)
+ this._opposite._done();
+
+ // EOF
+ this.push(null);
+ } else {
+ // Bail out
+ this.push('');
+ }
+ } else {
+ // Give them requested data
+ this.push(pool.slice(start, start + bytesRead));
+ }
+
+ // Let users know that we've some internal data to read
+ var halfRead = this._internallyPendingBytes() !== 0;
+
+ // Smart check to avoid invoking 'sslOutEnd' in the most of the cases
+ if (this._halfRead !== halfRead) {
+ this._halfRead = halfRead;
+
+ // Notify listeners about internal data end
+ if (!halfRead) {
+ if (this === this.pair.cleartext) {
+ debug('cleartext.sslOutEnd');
+ } else {
+ debug('encrypted.sslOutEnd');
+ }
+
+ this.emit('sslOutEnd');
+ }
+ }
+};
+
+
+CryptoStream.prototype.setTimeout = function(timeout, callback) {
+ if (this.socket) this.socket.setTimeout(timeout, callback);
+};
+
+
+CryptoStream.prototype.setNoDelay = function(noDelay) {
+ if (this.socket) this.socket.setNoDelay(noDelay);
+};
+
+
+CryptoStream.prototype.setKeepAlive = function(enable, initialDelay) {
+ if (this.socket) this.socket.setKeepAlive(enable, initialDelay);
+};
+
+CryptoStream.prototype.__defineGetter__('bytesWritten', function() {
+ return this.socket ? this.socket.bytesWritten : 0;
+});
+
+CryptoStream.prototype.getPeerCertificate = function() {
+ if (this.pair.ssl) {
+ var c = this.pair.ssl.getPeerCertificate();
+
+ if (c) {
+ if (c.issuer) c.issuer = tls.parseCertString(c.issuer);
+ if (c.subject) c.subject = tls.parseCertString(c.subject);
+ return c;
+ }
+ }
+
+ return null;
+};
+
+CryptoStream.prototype.getSession = function() {
+ if (this.pair.ssl) {
+ return this.pair.ssl.getSession();
+ }
+
+ return null;
+};
+
+CryptoStream.prototype.isSessionReused = function() {
+ if (this.pair.ssl) {
+ return this.pair.ssl.isSessionReused();
+ }
+
+ return null;
+};
+
+CryptoStream.prototype.getCipher = function(err) {
+ if (this.pair.ssl) {
+ return this.pair.ssl.getCurrentCipher();
+ } else {
+ return null;
+ }
+};
+
+
+CryptoStream.prototype.end = function(chunk, encoding) {
+ if (this === this.pair.cleartext) {
+ debug('cleartext.end');
+ } else {
+ debug('encrypted.end');
+ }
+
+ // Write pending data first
+ if (!util.isNull(this._pending)) this._writePending();
+
+ this.writable = false;
+
+ stream.Duplex.prototype.end.call(this, chunk, encoding);
+};
+
+
+CryptoStream.prototype.destroySoon = function(err) {
+ if (this === this.pair.cleartext) {
+ debug('cleartext.destroySoon');
+ } else {
+ debug('encrypted.destroySoon');
+ }
+
+ if (this.writable)
+ this.end();
+
+ if (this._writableState.finished && this._opposite._ended) {
+ this.destroy();
+ } else {
+ // Wait for both `finish` and `end` events to ensure that all data that
+ // was written on this side was read from the other side.
+ var self = this;
+ var waiting = 1;
+ function finish() {
+ if (--waiting === 0) self.destroy();
+ }
+ this._opposite.once('end', finish);
+ if (!this._finished) {
+ this.once('finish', finish);
+ ++waiting;
+ }
+ }
+};
+
+
+CryptoStream.prototype.destroy = function(err) {
+ if (this._destroyed) return;
+ this._destroyed = true;
+ this.readable = this.writable = false;
+
+ // Destroy both ends
+ if (this === this.pair.cleartext) {
+ debug('cleartext.destroy');
+ } else {
+ debug('encrypted.destroy');
+ }
+ this._opposite.destroy();
+
+ var self = this;
+ process.nextTick(function() {
+ // Force EOF
+ self.push(null);
+
+ // Emit 'close' event
+ self.emit('close', err ? true : false);
+ });
+};
+
+
+CryptoStream.prototype._done = function() {
+ this._doneFlag = true;
+
+ if (this === this.pair.encrypted && !this.pair._secureEstablished)
+ return this.pair.error();
+
+ if (this.pair.cleartext._doneFlag &&
+ this.pair.encrypted._doneFlag &&
+ !this.pair._doneFlag) {
+ // If both streams are done:
+ this.pair.destroy();
+ }
+};
+
+
+// readyState is deprecated. Don't use it.
+Object.defineProperty(CryptoStream.prototype, 'readyState', {
+ get: function() {
+ if (this._connecting) {
+ return 'opening';
+ } else if (this.readable && this.writable) {
+ return 'open';
+ } else if (this.readable && !this.writable) {
+ return 'readOnly';
+ } else if (!this.readable && this.writable) {
+ return 'writeOnly';
+ } else {
+ return 'closed';
+ }
+ }
+});
+
+
+function CleartextStream(pair, options) {
+ CryptoStream.call(this, pair, options);
+
+ // This is a fake kludge to support how the http impl sits
+ // on top of net Sockets
+ var self = this;
+ this._handle = {
+ readStop: function() {
+ self._reading = false;
+ },
+ readStart: function() {
+ if (self._reading && self._readableState.length > 0) return;
+ self._reading = true;
+ self.read(0);
+ if (self._opposite.readable) self._opposite.read(0);
+ }
+ };
+}
+util.inherits(CleartextStream, CryptoStream);
+
+
+CleartextStream.prototype._internallyPendingBytes = function() {
+ if (this.pair.ssl) {
+ return this.pair.ssl.clearPending();
+ } else {
+ return 0;
+ }
+};
+
+
+CleartextStream.prototype.address = function() {
+ return this.socket && this.socket.address();
+};
+
+
+CleartextStream.prototype.__defineGetter__('remoteAddress', function() {
+ return this.socket && this.socket.remoteAddress;
+});
+
+
+CleartextStream.prototype.__defineGetter__('remotePort', function() {
+ return this.socket && this.socket.remotePort;
+});
+
+
+CleartextStream.prototype.__defineGetter__('localAddress', function() {
+ return this.socket && this.socket.localAddress;
+});
+
+
+CleartextStream.prototype.__defineGetter__('localPort', function() {
+ return this.socket && this.socket.localPort;
+});
+
+
+function EncryptedStream(pair, options) {
+ CryptoStream.call(this, pair, options);
+}
+util.inherits(EncryptedStream, CryptoStream);
+
+
+EncryptedStream.prototype._internallyPendingBytes = function() {
+ if (this.pair.ssl) {
+ return this.pair.ssl.encPending();
+ } else {
+ return 0;
+ }
+};
+
+
+function onhandshakestart() {
+ debug('onhandshakestart');
+
+ var self = this;
+ var ssl = self.ssl;
+ var now = Timer.now();
+
+ assert(now >= ssl.lastHandshakeTime);
+
+ if ((now - ssl.lastHandshakeTime) >= tls.CLIENT_RENEG_WINDOW * 1000) {
+ ssl.handshakes = 0;
+ }
+
+ var first = (ssl.lastHandshakeTime === 0);
+ ssl.lastHandshakeTime = now;
+ if (first) return;
+
+ if (++ssl.handshakes > tls.CLIENT_RENEG_LIMIT) {
+ // Defer the error event to the next tick. We're being called from OpenSSL's
+ // state machine and OpenSSL is not re-entrant. We cannot allow the user's
+ // callback to destroy the connection right now, it would crash and burn.
+ setImmediate(function() {
+ var err = new Error('TLS session renegotiation attack detected.');
+ if (self.cleartext) self.cleartext.emit('error', err);
+ });
+ }
+}
+
+
+function onhandshakedone() {
+ // for future use
+ debug('onhandshakedone');
+}
+
+
+function onclienthello(hello) {
+ var self = this,
+ once = false;
+
+ this._resumingSession = true;
+ function callback(err, session) {
+ if (once) return;
+ once = true;
+
+ if (err) return self.socket.destroy(err);
+
+ self.ssl.loadSession(session);
+ self.ssl.endParser();
+
+ // Cycle data
+ self._resumingSession = false;
+ self.cleartext.read(0);
+ self.encrypted.read(0);
+ }
+
+ if (hello.sessionId.length <= 0 ||
+ !this.server ||
+ !this.server.emit('resumeSession', hello.sessionId, callback)) {
+ callback(null, null);
+ }
+}
+
+
+function onnewsession(key, session) {
+ if (!this.server) return;
+ this.server.emit('newSession', key, session);
+}
+
+
+/**
+ * Provides a pair of streams to do encrypted communication.
+ */
+
+function SecurePair(credentials, isServer, requestCert, rejectUnauthorized,
+ options) {
+ if (!(this instanceof SecurePair)) {
+ return new SecurePair(credentials,
+ isServer,
+ requestCert,
+ rejectUnauthorized,
+ options);
+ }
+
+ var self = this;
+
+ options || (options = {});
+
+ events.EventEmitter.call(this);
+
+ this.server = options.server;
+ this._secureEstablished = false;
+ this._isServer = isServer ? true : false;
+ this._encWriteState = true;
+ this._clearWriteState = true;
+ this._doneFlag = false;
+ this._destroying = false;
+
+ if (!credentials) {
+ this.credentials = crypto.createCredentials();
+ } else {
+ this.credentials = credentials;
+ }
+
+ if (!this._isServer) {
+ // For clients, we will always have either a given ca list or be using
+ // default one
+ requestCert = true;
+ }
+
+ this._rejectUnauthorized = rejectUnauthorized ? true : false;
+ this._requestCert = requestCert ? true : false;
+
+ this.ssl = new Connection(this.credentials.context,
+ this._isServer ? true : false,
+ this._isServer ? this._requestCert :
+ options.servername,
+ this._rejectUnauthorized);
+
+ if (this._isServer) {
+ this.ssl.onhandshakestart = onhandshakestart.bind(this);
+ this.ssl.onhandshakedone = onhandshakedone.bind(this);
+ this.ssl.onclienthello = onclienthello.bind(this);
+ this.ssl.onnewsession = onnewsession.bind(this);
+ this.ssl.lastHandshakeTime = 0;
+ this.ssl.handshakes = 0;
+ }
+
+ if (process.features.tls_sni) {
+ if (this._isServer && options.SNICallback) {
+ this.ssl.setSNICallback(options.SNICallback);
+ }
+ this.servername = null;
+ }
+
+ if (process.features.tls_npn && options.NPNProtocols) {
+ this.ssl.setNPNProtocols(options.NPNProtocols);
+ this.npnProtocol = null;
+ }
+
+ /* Acts as a r/w stream to the cleartext side of the stream. */
+ this.cleartext = new CleartextStream(this, options.cleartext);
+
+ /* Acts as a r/w stream to the encrypted side of the stream. */
+ this.encrypted = new EncryptedStream(this, options.encrypted);
+
+ /* Let streams know about each other */
+ this.cleartext._opposite = this.encrypted;
+ this.encrypted._opposite = this.cleartext;
+ this.cleartext.init();
+ this.encrypted.init();
+
+ process.nextTick(function() {
+ /* The Connection may be destroyed by an abort call */
+ if (self.ssl) {
+ self.ssl.start();
+ }
+ });
+}
+
+util.inherits(SecurePair, events.EventEmitter);
+
+
+exports.createSecurePair = function(credentials,
+ isServer,
+ requestCert,
+ rejectUnauthorized) {
+ var pair = new SecurePair(credentials,
+ isServer,
+ requestCert,
+ rejectUnauthorized);
+ return pair;
+};
+
+
+SecurePair.prototype.maybeInitFinished = function() {
+ if (this.ssl && !this._secureEstablished && this.ssl.isInitFinished()) {
+ if (process.features.tls_npn) {
+ this.npnProtocol = this.ssl.getNegotiatedProtocol();
+ }
+
+ if (process.features.tls_sni) {
+ this.servername = this.ssl.getServername();
+ }
+
+ this._secureEstablished = true;
+ debug('secure established');
+ this.emit('secure');
+ }
+};
+
+
+SecurePair.prototype.destroy = function() {
+ if (this._destroying) return;
+
+ if (!this._doneFlag) {
+ debug('SecurePair.destroy');
+ this._destroying = true;
+
+ // SecurePair should be destroyed only after it's streams
+ this.cleartext.destroy();
+ this.encrypted.destroy();
+
+ this._doneFlag = true;
+ this.ssl.error = null;
+ this.ssl.close();
+ this.ssl = null;
+ }
+};
+
+
+SecurePair.prototype.error = function(returnOnly) {
+ var err = this.ssl.error;
+ this.ssl.error = null;
+
+ if (!this._secureEstablished) {
+ // Emit ECONNRESET instead of zero return
+ if (!err || err.message === 'ZERO_RETURN') {
+ var connReset = new Error('socket hang up');
+ connReset.code = 'ECONNRESET';
+ connReset.sslError = err && err.message;
+
+ err = connReset;
+ }
+ this.destroy();
+ if (!returnOnly) this.emit('error', err);
+ } else if (this._isServer &&
+ this._rejectUnauthorized &&
+ /peer did not return a certificate/.test(err.message)) {
+ // Not really an error.
+ this.destroy();
+ } else {
+ if (!returnOnly) this.cleartext.emit('error', err);
+ }
+ return err;
+};
+
+
+exports.pipe = function pipe(pair, socket) {
+ pair.encrypted.pipe(socket);
+ socket.pipe(pair.encrypted);
+
+ pair.encrypted.on('close', function() {
+ process.nextTick(function() {
+ // Encrypted should be unpiped from socket to prevent possible
+ // write after destroy.
+ pair.encrypted.unpipe(socket);
++ socket.destroySoon();
+ });
+ });
+
+ pair.fd = socket.fd;
+ var cleartext = pair.cleartext;
+ cleartext.socket = socket;
+ cleartext.encrypted = pair.encrypted;
+ cleartext.authorized = false;
+
+ // cycle the data whenever the socket drains, so that
+ // we can pull some more into it. normally this would
+ // be handled by the fact that pipe() triggers read() calls
+ // on writable.drain, but CryptoStreams are a bit more
+ // complicated. Since the encrypted side actually gets
+ // its data from the cleartext side, we have to give it a
+ // light kick to get in motion again.
+ socket.on('drain', function() {
+ if (pair.encrypted._pending)
+ pair.encrypted._writePending();
+ if (pair.cleartext._pending)
+ pair.cleartext._writePending();
+ pair.encrypted.read(0);
+ pair.cleartext.read(0);
+ });
+
+ function onerror(e) {
+ if (cleartext._controlReleased) {
+ cleartext.emit('error', e);
+ }
+ }
+
+ function onclose() {
+ socket.removeListener('error', onerror);
+ socket.removeListener('timeout', ontimeout);
+ }
+
+ function ontimeout() {
+ cleartext.emit('timeout');
+ }
+
+ socket.on('error', onerror);
+ socket.on('close', onclose);
+ socket.on('timeout', ontimeout);
+
+ return cleartext;
+};
}
#endif
+void Connection::New(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
-class Cipher : public ObjectWrap {
- public:
- static void Initialize (v8::Handle<v8::Object> target) {
- HandleScope scope;
-
- Local<FunctionTemplate> t = FunctionTemplate::New(New);
-
- t->InstanceTemplate()->SetInternalFieldCount(1);
-
- NODE_SET_PROTOTYPE_METHOD(t, "init", CipherInit);
- NODE_SET_PROTOTYPE_METHOD(t, "initiv", CipherInitIv);
- NODE_SET_PROTOTYPE_METHOD(t, "update", CipherUpdate);
- NODE_SET_PROTOTYPE_METHOD(t, "setAutoPadding", SetAutoPadding);
- NODE_SET_PROTOTYPE_METHOD(t, "final", CipherFinal);
-
- target->Set(String::NewSymbol("Cipher"), t->GetFunction());
+ if (args.Length() < 1 || !args[0]->IsObject()) {
+ return ThrowError("First argument must be a crypto module Credentials");
}
+ SecureContext* sc = WeakObject::Unwrap<SecureContext>(args[0]->ToObject());
+ Environment* env = sc->env();
- bool CipherInit(char* cipherType, char* key_buf, int key_buf_len) {
- cipher = EVP_get_cipherbyname(cipherType);
- if(!cipher) {
- fprintf(stderr, "node-crypto : Unknown cipher %s\n", cipherType);
- return false;
- }
+ bool is_server = args[1]->BooleanValue();
- unsigned char key[EVP_MAX_KEY_LENGTH],iv[EVP_MAX_IV_LENGTH];
- int key_len = EVP_BytesToKey(cipher, EVP_md5(), NULL,
- (unsigned char*) key_buf, key_buf_len, 1, key, iv);
+ SSLWrap<Connection>::Kind kind =
+ is_server ? SSLWrap<Connection>::kServer : SSLWrap<Connection>::kClient;
+ Connection* conn = new Connection(env, args.This(), sc, kind);
+ conn->ssl_ = SSL_new(sc->ctx_);
+ conn->bio_read_ = NodeBIO::New();
+ conn->bio_write_ = NodeBIO::New();
- EVP_CIPHER_CTX_init(&ctx);
- EVP_CipherInit_ex(&ctx, cipher, NULL, NULL, NULL, true);
- if (!EVP_CIPHER_CTX_set_key_length(&ctx, key_len)) {
- fprintf(stderr, "node-crypto : Invalid key length %d\n", key_len);
- EVP_CIPHER_CTX_cleanup(&ctx);
- return false;
- }
- EVP_CipherInit_ex(&ctx, NULL, NULL,
- (unsigned char*)key,
- (unsigned char*)iv, true);
- initialised_ = true;
- return true;
- }
-
-
- bool CipherInitIv(char* cipherType,
- char* key,
- int key_len,
- char* iv,
- int iv_len) {
- cipher = EVP_get_cipherbyname(cipherType);
- if(!cipher) {
- fprintf(stderr, "node-crypto : Unknown cipher %s\n", cipherType);
- return false;
- }
- /* OpenSSL versions up to 0.9.8l failed to return the correct
- iv_length (0) for ECB ciphers */
- if (EVP_CIPHER_iv_length(cipher) != iv_len &&
- !(EVP_CIPHER_mode(cipher) == EVP_CIPH_ECB_MODE && iv_len == 0)) {
- fprintf(stderr, "node-crypto : Invalid IV length %d\n", iv_len);
- return false;
- }
- EVP_CIPHER_CTX_init(&ctx);
- EVP_CipherInit_ex(&ctx, cipher, NULL, NULL, NULL, true);
- if (!EVP_CIPHER_CTX_set_key_length(&ctx, key_len)) {
- fprintf(stderr, "node-crypto : Invalid key length %d\n", key_len);
- EVP_CIPHER_CTX_cleanup(&ctx);
- return false;
- }
- EVP_CipherInit_ex(&ctx, NULL, NULL,
- (unsigned char*)key,
- (unsigned char*)iv, true);
- initialised_ = true;
- return true;
- }
+ SSL_set_app_data(conn->ssl_, conn);
- int CipherUpdate(char* data, int len, unsigned char** out, int* out_len) {
- if (!initialised_) return 0;
- *out_len = len+EVP_CIPHER_CTX_block_size(&ctx);
- *out = new unsigned char[*out_len];
- return EVP_CipherUpdate(&ctx, *out, out_len, (unsigned char*)data, len);
- }
+ if (is_server)
+ SSL_set_info_callback(conn->ssl_, SSLInfoCallback);
- int SetAutoPadding(bool auto_padding) {
- if (!initialised_) return 0;
- return EVP_CIPHER_CTX_set_padding(&ctx, auto_padding ? 1 : 0);
+#ifdef OPENSSL_NPN_NEGOTIATED
+ if (is_server) {
+ // Server should advertise NPN protocols
+ SSL_CTX_set_next_protos_advertised_cb(
+ sc->ctx_,
+ SSLWrap<Connection>::AdvertiseNextProtoCallback,
+ conn);
+ } else {
+ // Client should select protocol from advertised
+ // If server supports NPN
+ SSL_CTX_set_next_proto_select_cb(
+ sc->ctx_,
+ SSLWrap<Connection>::SelectNextProtoCallback,
+ conn);
}
+#endif
- int CipherFinal(unsigned char** out, int *out_len) {
- if (!initialised_) return 0;
- *out = new unsigned char[EVP_CIPHER_CTX_block_size(&ctx)];
- int r = EVP_CipherFinal_ex(&ctx,*out, out_len);
- EVP_CIPHER_CTX_cleanup(&ctx);
- initialised_ = false;
- return r;
+#ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB
+ if (is_server) {
+ SSL_CTX_set_tlsext_servername_callback(sc->ctx_, SelectSNIContextCallback_);
+ } else {
+ const String::Utf8Value servername(args[2]);
+ SSL_set_tlsext_host_name(conn->ssl_, *servername);
}
+#endif
+ SSL_set_bio(conn->ssl_, conn->bio_read_, conn->bio_write_);
- protected:
-
- static Handle<Value> New(const Arguments& args) {
- HandleScope scope;
-
- Cipher *cipher = new Cipher();
- cipher->Wrap(args.This());
- return args.This();
- }
-
- static Handle<Value> CipherInit(const Arguments& args) {
- HandleScope scope;
+#ifdef SSL_MODE_RELEASE_BUFFERS
+ long mode = SSL_get_mode(conn->ssl_);
+ SSL_set_mode(conn->ssl_, mode | SSL_MODE_RELEASE_BUFFERS);
+#endif
- Cipher *cipher = ObjectWrap::Unwrap<Cipher>(args.This());
- if (args.Length() <= 1
- || !args[0]->IsString()
- || !(args[1]->IsString() || Buffer::HasInstance(args[1])))
- {
- return ThrowException(Exception::Error(String::New(
- "Must give cipher-type, key")));
+ int verify_mode;
+ if (is_server) {
+ bool request_cert = args[2]->BooleanValue();
+ if (!request_cert) {
+ // Note reject_unauthorized ignored.
+ verify_mode = SSL_VERIFY_NONE;
+ } else {
+ bool reject_unauthorized = args[3]->BooleanValue();
+ verify_mode = SSL_VERIFY_PEER;
+ if (reject_unauthorized)
+ verify_mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
}
+ } else {
+ // Note request_cert and reject_unauthorized are ignored for clients.
+ verify_mode = SSL_VERIFY_NONE;
+ }
- ASSERT_IS_BUFFER(args[1]);
- ssize_t key_buf_len = Buffer::Length(args[1]);
- if (key_buf_len < 0) {
- Local<Value> exception = Exception::TypeError(String::New("Bad argument"));
- return ThrowException(exception);
- }
+ // Always allow a connection. We'll reject in javascript.
+ SSL_set_verify(conn->ssl_, verify_mode, VerifyCallback);
- char* key_buf = new char[key_buf_len];
- ssize_t key_written = DecodeWrite(key_buf, key_buf_len, args[1], BINARY);
- assert(key_written == key_buf_len);
+ if (is_server) {
+ SSL_set_accept_state(conn->ssl_);
+ } else {
+ SSL_set_connect_state(conn->ssl_);
+ }
+}
- String::Utf8Value cipherType(args[0]);
- bool r = cipher->CipherInit(*cipherType, key_buf, key_buf_len);
+void Connection::SSLInfoCallback(const SSL *ssl_, int where, int ret) {
+ if (!(where & (SSL_CB_HANDSHAKE_START | SSL_CB_HANDSHAKE_DONE)))
+ return;
- delete [] key_buf;
+ // Be compatible with older versions of OpenSSL. SSL_get_app_data() wants
+ // a non-const SSL* in OpenSSL <= 0.9.7e.
+ SSL* ssl = const_cast<SSL*>(ssl_);
+ Connection* conn = static_cast<Connection*>(SSL_get_app_data(ssl));
+ Environment* env = conn->env();
+ Context::Scope context_scope(env->context());
+ HandleScope handle_scope(env->isolate());
- if (!r) return ThrowCryptoError(ERR_get_error());
+ if (where & SSL_CB_HANDSHAKE_START) {
+ MakeCallback(env,
+ conn->weak_object(node_isolate),
+ env->onhandshakestart_string());
+ }
- return args.This();
+ if (where & SSL_CB_HANDSHAKE_DONE) {
+ MakeCallback(env,
+ conn->weak_object(node_isolate),
+ env->onhandshakedone_string());
}
+}
- static Handle<Value> CipherInitIv(const Arguments& args) {
- Cipher *cipher = ObjectWrap::Unwrap<Cipher>(args.This());
+void Connection::EncIn(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
- HandleScope scope;
+ Connection* conn = Connection::Unwrap(args.This());
+ if (args.Length() < 3) {
+ return ThrowTypeError("Takes 3 parameters");
+ }
- if (args.Length() <= 2
- || !args[0]->IsString()
- || !(args[1]->IsString() || Buffer::HasInstance(args[1]))
- || !(args[2]->IsString() || Buffer::HasInstance(args[2])))
- {
- return ThrowException(Exception::Error(String::New(
- "Must give cipher-type, key, and iv as argument")));
- }
+ if (!Buffer::HasInstance(args[0])) {
+ return ThrowTypeError("Second argument should be a buffer");
+ }
- ASSERT_IS_BUFFER(args[1]);
- ssize_t key_len = Buffer::Length(args[1]);
+ char* buffer_data = Buffer::Data(args[0]);
+ size_t buffer_length = Buffer::Length(args[0]);
- if (key_len < 0) {
- Local<Value> exception = Exception::TypeError(String::New("Bad argument"));
- return ThrowException(exception);
- }
+ size_t off = args[1]->Int32Value();
+ size_t len = args[2]->Int32Value();
+ if (off + len > buffer_length) {
+ return ThrowError("off + len > buffer.length");
+ }
- ASSERT_IS_BUFFER(args[2]);
- ssize_t iv_len = Buffer::Length(args[2]);
+ int bytes_written;
+ char* data = buffer_data + off;
- if (iv_len < 0) {
- Local<Value> exception = Exception::TypeError(String::New("Bad argument"));
- return ThrowException(exception);
- }
+ if (conn->is_server() && !conn->hello_parser_.IsEnded()) {
+ // Just accumulate data, everything will be pushed to BIO later
+ if (conn->hello_parser_.IsPaused()) {
+ bytes_written = 0;
+ } else {
+ // Copy incoming data to the internal buffer
+ // (which has a size of the biggest possible TLS frame)
+ size_t available = sizeof(conn->hello_data_) - conn->hello_offset_;
+ size_t copied = len < available ? len : available;
+ memcpy(conn->hello_data_ + conn->hello_offset_, data, copied);
+ conn->hello_offset_ += copied;
- char* key_buf = new char[key_len];
- ssize_t key_written = DecodeWrite(key_buf, key_len, args[1], BINARY);
- assert(key_written == key_len);
+ conn->hello_parser_.Parse(conn->hello_data_, conn->hello_offset_);
+ bytes_written = copied;
+ }
+ } else {
+ bytes_written = BIO_write(conn->bio_read_, data, len);
+ conn->HandleBIOError(conn->bio_read_, "BIO_write", bytes_written);
+ conn->SetShutdownFlags();
+ }
- char* iv_buf = new char[iv_len];
- ssize_t iv_written = DecodeWrite(iv_buf, iv_len, args[2], BINARY);
- assert(iv_written == iv_len);
+ args.GetReturnValue().Set(bytes_written);
+}
- String::Utf8Value cipherType(args[0]);
- bool r = cipher->CipherInitIv(*cipherType, key_buf,key_len,iv_buf,iv_len);
+void Connection::ClearOut(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
- delete [] key_buf;
- delete [] iv_buf;
+ Connection* conn = Connection::Unwrap(args.This());
- if (!r) return ThrowCryptoError(ERR_get_error());
+ if (args.Length() < 3) {
+ return ThrowTypeError("Takes 3 parameters");
+ }
- return args.This();
+ if (!Buffer::HasInstance(args[0])) {
+ return ThrowTypeError("Second argument should be a buffer");
}
- static Handle<Value> CipherUpdate(const Arguments& args) {
- Cipher *cipher = ObjectWrap::Unwrap<Cipher>(args.This());
+ char* buffer_data = Buffer::Data(args[0]);
+ size_t buffer_length = Buffer::Length(args[0]);
- HandleScope scope;
+ size_t off = args[1]->Int32Value();
+ size_t len = args[2]->Int32Value();
+ if (off + len > buffer_length) {
+ return ThrowError("off + len > buffer.length");
+ }
- ASSERT_IS_STRING_OR_BUFFER(args[0]);
+ if (!SSL_is_init_finished(conn->ssl_)) {
+ int rv;
- // Only copy the data if we have to, because it's a string
- unsigned char* out = 0;
- int out_len = 0, r;
- if (args[0]->IsString()) {
- Local<String> string = args[0].As<String>();
- enum encoding encoding = ParseEncoding(args[1], BINARY);
- if (!StringBytes::IsValidString(string, encoding))
- return ThrowTypeError("Bad input string");
- size_t buflen = StringBytes::StorageSize(string, encoding);
- char* buf = new char[buflen];
- size_t written = StringBytes::Write(buf, buflen, string, encoding);
- r = cipher->CipherUpdate(buf, written, &out, &out_len);
- delete[] buf;
+ if (conn->is_server()) {
+ rv = SSL_accept(conn->ssl_);
+ conn->HandleSSLError("SSL_accept:ClearOut",
+ rv,
+ kZeroIsAnError,
+ kSyscallError);
} else {
- char* buf = Buffer::Data(args[0]);
- size_t buflen = Buffer::Length(args[0]);
- r = cipher->CipherUpdate(buf, buflen, &out, &out_len);
+ rv = SSL_connect(conn->ssl_);
+ conn->HandleSSLError("SSL_connect:ClearOut",
+ rv,
+ kZeroIsAnError,
+ kSyscallError);
}
- if (r == 0) {
- delete[] out;
- return ThrowCryptoTypeError(ERR_get_error());
+ if (rv < 0) {
+ return args.GetReturnValue().Set(rv);
}
-
- Local<Value> outString;
- outString = Encode(out, out_len, BUFFER);
-
- if (out) delete[] out;
-
- return scope.Close(outString);
}
- static Handle<Value> SetAutoPadding(const Arguments& args) {
- HandleScope scope;
- Cipher *cipher = ObjectWrap::Unwrap<Cipher>(args.This());
+ int bytes_read = SSL_read(conn->ssl_, buffer_data + off, len);
+ conn->HandleSSLError("SSL_read:ClearOut",
+ bytes_read,
+ kZeroIsNotAnError,
+ kSyscallError);
+ conn->SetShutdownFlags();
- cipher->SetAutoPadding(args.Length() < 1 || args[0]->BooleanValue());
-
- return Undefined();
- }
+ args.GetReturnValue().Set(bytes_read);
+}
- static Handle<Value> CipherFinal(const Arguments& args) {
- Cipher *cipher = ObjectWrap::Unwrap<Cipher>(args.This());
- HandleScope scope;
+void Connection::ClearPending(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
+ Connection* conn = Connection::Unwrap(args.This());
+ int bytes_pending = BIO_pending(conn->bio_read_);
+ args.GetReturnValue().Set(bytes_pending);
+}
- unsigned char* out_value = NULL;
- int out_len = -1;
- Local<Value> outString ;
- int r = cipher->CipherFinal(&out_value, &out_len);
+void Connection::EncPending(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
+ Connection* conn = Connection::Unwrap(args.This());
+ int bytes_pending = BIO_pending(conn->bio_write_);
+ args.GetReturnValue().Set(bytes_pending);
+}
- if (out_len <= 0 || r == 0) {
- delete[] out_value;
- out_value = NULL;
- if (r == 0) return ThrowCryptoTypeError(ERR_get_error());
- }
- outString = Encode(out_value, out_len, BUFFER);
+void Connection::EncOut(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
- delete [] out_value;
- return scope.Close(outString);
- }
+ Connection* conn = Connection::Unwrap(args.This());
- Cipher () : ObjectWrap ()
- {
- initialised_ = false;
+ if (args.Length() < 3) {
+ return ThrowTypeError("Takes 3 parameters");
}
- ~Cipher () {
- if (initialised_) {
- EVP_CIPHER_CTX_cleanup(&ctx);
- }
+ if (!Buffer::HasInstance(args[0])) {
+ return ThrowTypeError("Second argument should be a buffer");
}
- private:
+ char* buffer_data = Buffer::Data(args[0]);
+ size_t buffer_length = Buffer::Length(args[0]);
- EVP_CIPHER_CTX ctx; /* coverity[member_decl] */
- const EVP_CIPHER *cipher; /* coverity[member_decl] */
- bool initialised_;
-};
+ size_t off = args[1]->Int32Value();
+ size_t len = args[2]->Int32Value();
+ if (off + len > buffer_length) {
+ return ThrowError("off + len > buffer.length");
+ }
+ int bytes_read = BIO_read(conn->bio_write_, buffer_data + off, len);
+ conn->HandleBIOError(conn->bio_write_, "BIO_read:EncOut", bytes_read);
+ conn->SetShutdownFlags();
-class Decipher : public ObjectWrap {
- public:
- static void
- Initialize (v8::Handle<v8::Object> target)
- {
- HandleScope scope;
+ args.GetReturnValue().Set(bytes_read);
+}
- Local<FunctionTemplate> t = FunctionTemplate::New(New);
- t->InstanceTemplate()->SetInternalFieldCount(1);
+void Connection::ClearIn(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
- NODE_SET_PROTOTYPE_METHOD(t, "init", DecipherInit);
- NODE_SET_PROTOTYPE_METHOD(t, "initiv", DecipherInitIv);
- NODE_SET_PROTOTYPE_METHOD(t, "update", DecipherUpdate);
- NODE_SET_PROTOTYPE_METHOD(t, "final", DecipherFinal);
- NODE_SET_PROTOTYPE_METHOD(t, "finaltol", DecipherFinal); // remove someday
- NODE_SET_PROTOTYPE_METHOD(t, "setAutoPadding", SetAutoPadding);
+ Connection* conn = Connection::Unwrap(args.This());
- target->Set(String::NewSymbol("Decipher"), t->GetFunction());
+ if (args.Length() < 3) {
+ return ThrowTypeError("Takes 3 parameters");
}
- bool DecipherInit(char* cipherType, char* key_buf, int key_buf_len) {
- cipher_ = EVP_get_cipherbyname(cipherType);
-
- if(!cipher_) {
- fprintf(stderr, "node-crypto : Unknown cipher %s\n", cipherType);
- return false;
- }
-
- unsigned char key[EVP_MAX_KEY_LENGTH],iv[EVP_MAX_IV_LENGTH];
- int key_len = EVP_BytesToKey(cipher_,
- EVP_md5(),
- NULL,
- (unsigned char*)(key_buf),
- key_buf_len,
- 1,
- key,
- iv);
-
- EVP_CIPHER_CTX_init(&ctx);
- EVP_CipherInit_ex(&ctx, cipher_, NULL, NULL, NULL, false);
- if (!EVP_CIPHER_CTX_set_key_length(&ctx, key_len)) {
- fprintf(stderr, "node-crypto : Invalid key length %d\n", key_len);
- EVP_CIPHER_CTX_cleanup(&ctx);
- return false;
- }
- EVP_CipherInit_ex(&ctx, NULL, NULL,
- (unsigned char*)key,
- (unsigned char*)iv, false);
- initialised_ = true;
- return true;
- }
-
-
- bool DecipherInitIv(char* cipherType,
- char* key,
- int key_len,
- char* iv,
- int iv_len) {
- cipher_ = EVP_get_cipherbyname(cipherType);
- if(!cipher_) {
- fprintf(stderr, "node-crypto : Unknown cipher %s\n", cipherType);
- return false;
- }
- /* OpenSSL versions up to 0.9.8l failed to return the correct
- iv_length (0) for ECB ciphers */
- if (EVP_CIPHER_iv_length(cipher_) != iv_len &&
- !(EVP_CIPHER_mode(cipher_) == EVP_CIPH_ECB_MODE && iv_len == 0)) {
- fprintf(stderr, "node-crypto : Invalid IV length %d\n", iv_len);
- return false;
- }
- EVP_CIPHER_CTX_init(&ctx);
- EVP_CipherInit_ex(&ctx, cipher_, NULL, NULL, NULL, false);
- if (!EVP_CIPHER_CTX_set_key_length(&ctx, key_len)) {
- fprintf(stderr, "node-crypto : Invalid key length %d\n", key_len);
- EVP_CIPHER_CTX_cleanup(&ctx);
- return false;
- }
- EVP_CipherInit_ex(&ctx, NULL, NULL,
- (unsigned char*)key,
- (unsigned char*)iv, false);
- initialised_ = true;
- return true;
- }
-
- int DecipherUpdate(char* data, int len, unsigned char** out, int* out_len) {
- if (!initialised_) {
- *out_len = 0;
- *out = NULL;
- return 0;
- }
-
- *out_len = len+EVP_CIPHER_CTX_block_size(&ctx);
- *out = new unsigned char[*out_len];
-
- return EVP_CipherUpdate(&ctx, *out, out_len, (unsigned char*)data, len);
+ if (!Buffer::HasInstance(args[0])) {
+ return ThrowTypeError("Second argument should be a buffer");
}
- int SetAutoPadding(bool auto_padding) {
- if (!initialised_) return 0;
- return EVP_CIPHER_CTX_set_padding(&ctx, auto_padding ? 1 : 0);
- }
+ char* buffer_data = Buffer::Data(args[0]);
+ size_t buffer_length = Buffer::Length(args[0]);
- // coverity[alloc_arg]
- int DecipherFinal(unsigned char** out, int *out_len) {
- int r;
+ size_t off = args[1]->Int32Value();
+ size_t len = args[2]->Int32Value();
+ if (off + len > buffer_length) {
+ return ThrowError("off + len > buffer.length");
+ }
- if (!initialised_) {
- *out_len = 0;
- *out = NULL;
- return 0;
+ if (!SSL_is_init_finished(conn->ssl_)) {
+ int rv;
+ if (conn->is_server()) {
+ rv = SSL_accept(conn->ssl_);
+ conn->HandleSSLError("SSL_accept:ClearIn",
+ rv,
+ kZeroIsAnError,
+ kSyscallError);
+ } else {
+ rv = SSL_connect(conn->ssl_);
+ conn->HandleSSLError("SSL_connect:ClearIn",
+ rv,
+ kZeroIsAnError,
+ kSyscallError);
}
- *out = new unsigned char[EVP_CIPHER_CTX_block_size(&ctx)];
- r = EVP_CipherFinal_ex(&ctx,*out,out_len);
- EVP_CIPHER_CTX_cleanup(&ctx);
- initialised_ = false;
- return r;
+ if (rv < 0) {
+ return args.GetReturnValue().Set(rv);
+ }
}
+ int bytes_written = SSL_write(conn->ssl_, buffer_data + off, len);
- protected:
+ conn->HandleSSLError("SSL_write:ClearIn",
+ bytes_written,
+ len == 0 ? kZeroIsNotAnError : kZeroIsAnError,
+ kSyscallError);
+ conn->SetShutdownFlags();
- static Handle<Value> New (const Arguments& args) {
- HandleScope scope;
-
- Decipher *cipher = new Decipher();
- cipher->Wrap(args.This());
- return args.This();
- }
-
- static Handle<Value> DecipherInit(const Arguments& args) {
- Decipher *cipher = ObjectWrap::Unwrap<Decipher>(args.This());
+ args.GetReturnValue().Set(bytes_written);
+}
- HandleScope scope;
- if (args.Length() <= 1
- || !args[0]->IsString()
- || !(args[1]->IsString() || Buffer::HasInstance(args[1])))
- {
- return ThrowException(Exception::Error(String::New(
- "Must give cipher-type, key as argument")));
- }
+void Connection::Start(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
- ASSERT_IS_BUFFER(args[1]);
- ssize_t key_len = Buffer::Length(args[1]);
+ Connection* conn = Connection::Unwrap(args.This());
- if (key_len < 0) {
- Local<Value> exception = Exception::TypeError(String::New("Bad argument"));
- return ThrowException(exception);
+ int rv = 0;
+ if (!SSL_is_init_finished(conn->ssl_)) {
+ if (conn->is_server()) {
+ rv = SSL_accept(conn->ssl_);
+ conn->HandleSSLError("SSL_accept:Start",
+ rv,
+ kZeroIsAnError,
+ kSyscallError);
+ } else {
+ rv = SSL_connect(conn->ssl_);
+ conn->HandleSSLError("SSL_connect:Start",
+ rv,
+ kZeroIsAnError,
+ kSyscallError);
}
+ }
+ args.GetReturnValue().Set(rv);
+}
- char* key_buf = new char[key_len];
- ssize_t key_written = DecodeWrite(key_buf, key_len, args[1], BINARY);
- assert(key_written == key_len);
-
- String::Utf8Value cipherType(args[0]);
-
- bool r = cipher->DecipherInit(*cipherType, key_buf,key_len);
- delete [] key_buf;
+void Connection::Shutdown(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
- if (!r) {
- return ThrowException(Exception::Error(String::New("DecipherInit error")));
- }
+ Connection* conn = Connection::Unwrap(args.This());
- return args.This();
+ if (conn->ssl_ == NULL) {
+ return args.GetReturnValue().Set(false);
}
- static Handle<Value> DecipherInitIv(const Arguments& args) {
- Decipher *cipher = ObjectWrap::Unwrap<Decipher>(args.This());
-
- HandleScope scope;
+ int rv = SSL_shutdown(conn->ssl_);
+ conn->HandleSSLError("SSL_shutdown", rv, kZeroIsNotAnError, kIgnoreSyscall);
+ conn->SetShutdownFlags();
+ args.GetReturnValue().Set(rv);
+}
- if (args.Length() <= 2
- || !args[0]->IsString()
- || !(args[1]->IsString() || Buffer::HasInstance(args[1]))
- || !(args[2]->IsString() || Buffer::HasInstance(args[2])))
- {
- return ThrowException(Exception::Error(String::New(
- "Must give cipher-type, key, and iv as argument")));
- }
- ASSERT_IS_BUFFER(args[1]);
- ssize_t key_len = Buffer::Length(args[1]);
+void Connection::Close(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
- if (key_len < 0) {
- Local<Value> exception = Exception::TypeError(String::New("Bad argument"));
- return ThrowException(exception);
- }
+ Connection* conn = Connection::Unwrap(args.This());
- ASSERT_IS_BUFFER(args[2]);
- ssize_t iv_len = Buffer::Length(args[2]);
+ if (conn->ssl_ != NULL) {
+ SSL_free(conn->ssl_);
+ conn->ssl_ = NULL;
+ }
+}
- if (iv_len < 0) {
- Local<Value> exception = Exception::TypeError(String::New("Bad argument"));
- return ThrowException(exception);
- }
- char* key_buf = new char[key_len];
- ssize_t key_written = DecodeWrite(key_buf, key_len, args[1], BINARY);
- assert(key_written == key_len);
+#ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB
+void Connection::GetServername(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
- char* iv_buf = new char[iv_len];
- ssize_t iv_written = DecodeWrite(iv_buf, iv_len, args[2], BINARY);
- assert(iv_written == iv_len);
+ Connection* conn = Connection::Unwrap(args.This());
- String::Utf8Value cipherType(args[0]);
+ if (conn->is_server() && !conn->servername_.IsEmpty()) {
+ args.GetReturnValue().Set(conn->servername_);
+ } else {
+ args.GetReturnValue().Set(false);
+ }
+}
- bool r = cipher->DecipherInitIv(*cipherType, key_buf,key_len,iv_buf,iv_len);
- delete [] key_buf;
- delete [] iv_buf;
+void Connection::SetSNICallback(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
- if (!r) {
- return ThrowException(Exception::Error(String::New("DecipherInitIv error")));
- }
+ Connection* conn = Connection::Unwrap(args.This());
- return args.This();
+ if (args.Length() < 1 || !args[0]->IsFunction()) {
+ return ThrowError("Must give a Function as first argument");
}
- static Handle<Value> DecipherUpdate(const Arguments& args) {
- HandleScope scope;
+ Local<Object> obj = Object::New();
+ obj->Set(FIXED_ONE_BYTE_STRING(node_isolate, "onselect"), args[0]);
+ conn->sniObject_.Reset(node_isolate, obj);
+}
+#endif
- Decipher *cipher = ObjectWrap::Unwrap<Decipher>(args.This());
- ASSERT_IS_STRING_OR_BUFFER(args[0]);
+void CipherBase::Initialize(Environment* env, Handle<Object> target) {
+ Local<FunctionTemplate> t = FunctionTemplate::New(New);
- // Only copy the data if we have to, because it's a string
- unsigned char* out = 0;
- int out_len = 0, r;
- if (args[0]->IsString()) {
- Local<String> string = args[0].As<String>();
- enum encoding encoding = ParseEncoding(args[1], BINARY);
- if (!StringBytes::IsValidString(string, encoding))
- return ThrowTypeError("Bad input string");
- size_t buflen = StringBytes::StorageSize(string, encoding);
- char* buf = new char[buflen];
- size_t written = StringBytes::Write(buf, buflen, string, encoding);
- r = cipher->DecipherUpdate(buf, written, &out, &out_len);
- delete[] buf;
- } else {
- char* buf = Buffer::Data(args[0]);
- size_t buflen = Buffer::Length(args[0]);
- r = cipher->DecipherUpdate(buf, buflen, &out, &out_len);
- }
+ t->InstanceTemplate()->SetInternalFieldCount(1);
- if (r == 0) {
- delete[] out;
- return ThrowCryptoTypeError(ERR_get_error());
- }
+ NODE_SET_PROTOTYPE_METHOD(t, "init", Init);
+ NODE_SET_PROTOTYPE_METHOD(t, "initiv", InitIv);
+ NODE_SET_PROTOTYPE_METHOD(t, "update", Update);
+ NODE_SET_PROTOTYPE_METHOD(t, "final", Final);
+ NODE_SET_PROTOTYPE_METHOD(t, "setAutoPadding", SetAutoPadding);
- Local<Value> outString;
- outString = Encode(out, out_len, BUFFER);
+ target->Set(FIXED_ONE_BYTE_STRING(node_isolate, "CipherBase"),
+ t->GetFunction());
+}
- if (out) delete [] out;
- return scope.Close(outString);
- }
+void CipherBase::New(const FunctionCallbackInfo<Value>& args) {
+ assert(args.IsConstructCall() == true);
+ CipherKind kind = args[0]->IsTrue() ? kCipher : kDecipher;
+ new CipherBase(args.GetIsolate(), args.This(), kind);
+}
- static Handle<Value> SetAutoPadding(const Arguments& args) {
- HandleScope scope;
- Decipher *cipher = ObjectWrap::Unwrap<Decipher>(args.This());
- cipher->SetAutoPadding(args.Length() < 1 || args[0]->BooleanValue());
+void CipherBase::Init(const char* cipher_type,
+ const char* key_buf,
+ int key_buf_len) {
+ HandleScope scope(node_isolate);
- return Undefined();
+ assert(cipher_ == NULL);
+ cipher_ = EVP_get_cipherbyname(cipher_type);
+ if (cipher_ == NULL) {
+ return ThrowError("Unknown cipher");
}
- static Handle<Value> DecipherFinal(const Arguments& args) {
- HandleScope scope;
+ unsigned char key[EVP_MAX_KEY_LENGTH];
+ unsigned char iv[EVP_MAX_IV_LENGTH];
- Decipher *cipher = ObjectWrap::Unwrap<Decipher>(args.This());
+ int key_len = EVP_BytesToKey(cipher_,
+ EVP_md5(),
+ NULL,
+ reinterpret_cast<const unsigned char*>(key_buf),
+ key_buf_len,
+ 1,
+ key,
+ iv);
- unsigned char* out_value = NULL;
- int out_len = -1;
- Local<Value> outString;
+ EVP_CIPHER_CTX_init(&ctx_);
+ EVP_CipherInit_ex(&ctx_, cipher_, NULL, NULL, NULL, kind_ == kCipher);
+ if (!EVP_CIPHER_CTX_set_key_length(&ctx_, key_len)) {
+ EVP_CIPHER_CTX_cleanup(&ctx_);
+ return ThrowError("Invalid key length");
+ }
- int r = cipher->DecipherFinal(&out_value, &out_len);
+ EVP_CipherInit_ex(&ctx_,
+ NULL,
+ NULL,
+ reinterpret_cast<unsigned char*>(key),
+ reinterpret_cast<unsigned char*>(iv),
+ kind_ == kCipher);
+ initialised_ = true;
+}
- if (out_len <= 0 || r == 0) {
- delete [] out_value; // allocated even if out_len == 0
- out_value = NULL;
- if (r == 0) return ThrowCryptoTypeError(ERR_get_error());
- }
- outString = Encode(out_value, out_len, BUFFER);
- delete [] out_value;
- return scope.Close(outString);
- }
+void CipherBase::Init(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
- Decipher () : ObjectWrap () {
- initialised_ = false;
- }
+ CipherBase* cipher = WeakObject::Unwrap<CipherBase>(args.This());
- ~Decipher () {
- if (initialised_) {
- EVP_CIPHER_CTX_cleanup(&ctx);
- }
+ if (args.Length() < 2 ||
+ !(args[0]->IsString() && Buffer::HasInstance(args[1]))) {
+ return ThrowError("Must give cipher-type, key");
}
- private:
+ const String::Utf8Value cipher_type(args[0]);
+ const char* key_buf = Buffer::Data(args[1]);
+ ssize_t key_buf_len = Buffer::Length(args[1]);
+ cipher->Init(*cipher_type, key_buf, key_buf_len);
+}
- EVP_CIPHER_CTX ctx;
- const EVP_CIPHER *cipher_;
- bool initialised_;
-};
+void CipherBase::InitIv(const char* cipher_type,
+ const char* key,
+ int key_len,
+ const char* iv,
+ int iv_len) {
+ HandleScope scope(node_isolate);
+ cipher_ = EVP_get_cipherbyname(cipher_type);
+ if (cipher_ == NULL) {
+ return ThrowError("Unknown cipher");
+ }
+ /* OpenSSL versions up to 0.9.8l failed to return the correct
+ iv_length (0) for ECB ciphers */
+ if (EVP_CIPHER_iv_length(cipher_) != iv_len &&
+ !(EVP_CIPHER_mode(cipher_) == EVP_CIPH_ECB_MODE && iv_len == 0)) {
+ return ThrowError("Invalid IV length");
+ }
+ EVP_CIPHER_CTX_init(&ctx_);
+ EVP_CipherInit_ex(&ctx_, cipher_, NULL, NULL, NULL, kind_ == kCipher);
+ if (!EVP_CIPHER_CTX_set_key_length(&ctx_, key_len)) {
+ EVP_CIPHER_CTX_cleanup(&ctx_);
+ return ThrowError("Invalid key length");
+ }
-class Hmac : public ObjectWrap {
- public:
- static void Initialize (v8::Handle<v8::Object> target) {
- HandleScope scope;
+ EVP_CipherInit_ex(&ctx_,
+ NULL,
+ NULL,
+ reinterpret_cast<const unsigned char*>(key),
+ reinterpret_cast<const unsigned char*>(iv),
+ kind_ == kCipher);
+ initialised_ = true;
+}
- Local<FunctionTemplate> t = FunctionTemplate::New(New);
- t->InstanceTemplate()->SetInternalFieldCount(1);
+void CipherBase::InitIv(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
- NODE_SET_PROTOTYPE_METHOD(t, "init", HmacInit);
- NODE_SET_PROTOTYPE_METHOD(t, "update", HmacUpdate);
- NODE_SET_PROTOTYPE_METHOD(t, "digest", HmacDigest);
+ CipherBase* cipher = WeakObject::Unwrap<CipherBase>(args.This());
- target->Set(String::NewSymbol("Hmac"), t->GetFunction());
+ if (args.Length() < 3 || !args[0]->IsString()) {
+ return ThrowError("Must give cipher-type, key, and iv as argument");
}
- bool HmacInit(char* hashType, char* key, int key_len) {
- md = EVP_get_digestbyname(hashType);
- if(!md) {
- fprintf(stderr, "node-crypto : Unknown message digest %s\n", hashType);
- return false;
- }
- HMAC_CTX_init(&ctx);
- if (key_len == 0) {
- HMAC_Init(&ctx, "", 0, md);
- } else {
- HMAC_Init(&ctx, key, key_len, md);
- }
- initialised_ = true;
- return true;
+ ASSERT_IS_BUFFER(args[1]);
+ ASSERT_IS_BUFFER(args[2]);
- }
+ const String::Utf8Value cipher_type(args[0]);
+ ssize_t key_len = Buffer::Length(args[1]);
+ const char* key_buf = Buffer::Data(args[1]);
+ ssize_t iv_len = Buffer::Length(args[2]);
+ const char* iv_buf = Buffer::Data(args[2]);
+ cipher->InitIv(*cipher_type, key_buf, key_len, iv_buf, iv_len);
+}
- int HmacUpdate(char* data, int len) {
- if (!initialised_) return 0;
- HMAC_Update(&ctx, (unsigned char*)data, len);
- return 1;
- }
- int HmacDigest(unsigned char** md_value, unsigned int *md_len) {
- if (!initialised_) return 0;
- *md_value = new unsigned char[EVP_MAX_MD_SIZE];
- HMAC_Final(&ctx, *md_value, md_len);
- HMAC_CTX_cleanup(&ctx);
- initialised_ = false;
- return 1;
+bool CipherBase::Update(const char* data,
+ int len,
+ unsigned char** out,
+ int* out_len) {
+ if (!initialised_)
+ return 0;
+ *out_len = len + EVP_CIPHER_CTX_block_size(&ctx_);
+ *out = new unsigned char[*out_len];
+ return EVP_CipherUpdate(&ctx_,
+ *out,
+ out_len,
+ reinterpret_cast<const unsigned char*>(data),
+ len);
+}
+
+
+void CipherBase::Update(const FunctionCallbackInfo<Value>& args) {
+ Environment* env = Environment::GetCurrent(args.GetIsolate());
+ HandleScope handle_scope(args.GetIsolate());
+
+ CipherBase* cipher = WeakObject::Unwrap<CipherBase>(args.This());
+
+ ASSERT_IS_STRING_OR_BUFFER(args[0]);
+
+ unsigned char* out = NULL;
+ bool r;
+ int out_len = 0;
+
+ // Only copy the data if we have to, because it's a string
+ if (args[0]->IsString()) {
+ Local<String> string = args[0].As<String>();
+ enum encoding encoding = ParseEncoding(args[1], BINARY);
+ if (!StringBytes::IsValidString(string, encoding))
+ return ThrowTypeError("Bad input string");
+ size_t buflen = StringBytes::StorageSize(string, encoding);
+ char* buf = new char[buflen];
+ size_t written = StringBytes::Write(buf, buflen, string, encoding);
+ r = cipher->Update(buf, written, &out, &out_len);
+ delete[] buf;
+ } else {
+ char* buf = Buffer::Data(args[0]);
+ size_t buflen = Buffer::Length(args[0]);
+ r = cipher->Update(buf, buflen, &out, &out_len);
}
+ if (!r) {
+ delete[] out;
+ return ThrowCryptoTypeError(ERR_get_error());
+ }
- protected:
+ Local<Object> buf = Buffer::New(env, reinterpret_cast<char*>(out), out_len);
+ if (out)
+ delete[] out;
- static Handle<Value> New (const Arguments& args) {
- HandleScope scope;
+ args.GetReturnValue().Set(buf);
+}
- Hmac *hmac = new Hmac();
- hmac->Wrap(args.This());
- return args.This();
- }
- static Handle<Value> HmacInit(const Arguments& args) {
- Hmac *hmac = ObjectWrap::Unwrap<Hmac>(args.This());
+bool CipherBase::SetAutoPadding(bool auto_padding) {
+ if (!initialised_)
+ return false;
+ return EVP_CIPHER_CTX_set_padding(&ctx_, auto_padding);
+}
- HandleScope scope;
- if (args.Length() == 0 || !args[0]->IsString()) {
- return ThrowException(Exception::Error(String::New(
- "Must give hashtype string as argument")));
- }
+void CipherBase::SetAutoPadding(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
+ CipherBase* cipher = WeakObject::Unwrap<CipherBase>(args.This());
+ cipher->SetAutoPadding(args.Length() < 1 || args[0]->BooleanValue());
+}
- ASSERT_IS_BUFFER(args[1]);
- ssize_t len = Buffer::Length(args[1]);
- if (len < 0) {
- Local<Value> exception = Exception::TypeError(String::New("Bad argument"));
- return ThrowException(exception);
- }
+bool CipherBase::Final(unsigned char** out, int *out_len) {
+ if (!initialised_)
+ return false;
- String::Utf8Value hashType(args[0]);
+ *out = new unsigned char[EVP_CIPHER_CTX_block_size(&ctx_)];
+ bool r = EVP_CipherFinal_ex(&ctx_, *out, out_len);
+ EVP_CIPHER_CTX_cleanup(&ctx_);
+ initialised_ = false;
- bool r;
+ return r;
+}
- if( Buffer::HasInstance(args[1])) {
- char* buffer_data = Buffer::Data(args[1]);
- size_t buffer_length = Buffer::Length(args[1]);
- r = hmac->HmacInit(*hashType, buffer_data, buffer_length);
- } else {
- char* buf = new char[len];
- ssize_t written = DecodeWrite(buf, len, args[1], BINARY);
- assert(written == len);
+void CipherBase::Final(const FunctionCallbackInfo<Value>& args) {
+ Environment* env = Environment::GetCurrent(args.GetIsolate());
+ HandleScope handle_scope(args.GetIsolate());
- r = hmac->HmacInit(*hashType, buf, len);
+ CipherBase* cipher = WeakObject::Unwrap<CipherBase>(args.This());
- delete [] buf;
- }
+ unsigned char* out_value = NULL;
+ int out_len = -1;
+ Local<Value> outString;
- if (!r) {
- return ThrowException(Exception::Error(String::New("hmac error")));
- }
+ bool r = cipher->Final(&out_value, &out_len);
- return args.This();
+ if (out_len <= 0 || !r) {
+ delete[] out_value;
+ out_value = NULL;
+ out_len = 0;
+ if (!r)
+ return ThrowCryptoTypeError(ERR_get_error());
}
- static Handle<Value> HmacUpdate(const Arguments& args) {
- Hmac *hmac = ObjectWrap::Unwrap<Hmac>(args.This());
+ args.GetReturnValue().Set(
+ Buffer::New(env, reinterpret_cast<char*>(out_value), out_len));
+}
- HandleScope scope;
- ASSERT_IS_STRING_OR_BUFFER(args[0]);
+void Hmac::Initialize(Environment* env, v8::Handle<v8::Object> target) {
+ Local<FunctionTemplate> t = FunctionTemplate::New(New);
- // Only copy the data if we have to, because it's a string
- int r;
- if (args[0]->IsString()) {
- Local<String> string = args[0].As<String>();
- enum encoding encoding = ParseEncoding(args[1], BINARY);
- if (!StringBytes::IsValidString(string, encoding))
- return ThrowTypeError("Bad input string");
- size_t buflen = StringBytes::StorageSize(string, encoding);
- char* buf = new char[buflen];
- size_t written = StringBytes::Write(buf, buflen, string, encoding);
- r = hmac->HmacUpdate(buf, written);
- delete[] buf;
- } else {
- char* buf = Buffer::Data(args[0]);
- size_t buflen = Buffer::Length(args[0]);
- r = hmac->HmacUpdate(buf, buflen);
- }
+ t->InstanceTemplate()->SetInternalFieldCount(1);
- if (!r) {
- Local<Value> exception = Exception::TypeError(String::New("HmacUpdate fail"));
- return ThrowException(exception);
- }
+ NODE_SET_PROTOTYPE_METHOD(t, "init", HmacInit);
+ NODE_SET_PROTOTYPE_METHOD(t, "update", HmacUpdate);
+ NODE_SET_PROTOTYPE_METHOD(t, "digest", HmacDigest);
- return args.This();
- }
+ target->Set(FIXED_ONE_BYTE_STRING(node_isolate, "Hmac"), t->GetFunction());
+}
- static Handle<Value> HmacDigest(const Arguments& args) {
- Hmac *hmac = ObjectWrap::Unwrap<Hmac>(args.This());
- HandleScope scope;
+void Hmac::New(const FunctionCallbackInfo<Value>& args) {
+ new Hmac(args.GetIsolate(), args.This());
+}
- enum encoding encoding = BUFFER;
- if (args.Length() >= 1) {
- encoding = ParseEncoding(args[0]->ToString(), BUFFER);
- }
- unsigned char* md_value = NULL;
- unsigned int md_len = 0;
- Local<Value> outString;
+void Hmac::HmacInit(const char* hash_type, const char* key, int key_len) {
+ HandleScope scope(node_isolate);
- int r = hmac->HmacDigest(&md_value, &md_len);
- if (r == 0) {
- md_value = NULL;
- md_len = 0;
- }
+ assert(md_ == NULL);
+ md_ = EVP_get_digestbyname(hash_type);
+ if (md_ == NULL) {
+ return ThrowError("Unknown message digest");
+ }
+ HMAC_CTX_init(&ctx_);
+ if (key_len == 0) {
+ HMAC_Init(&ctx_, "", 0, md_);
+ } else {
+ HMAC_Init(&ctx_, key, key_len, md_);
+ }
+ initialised_ = true;
+}
- outString = StringBytes::Encode(
- reinterpret_cast<const char*>(md_value), md_len, encoding);
- delete[] md_value;
- return scope.Close(outString);
- }
+void Hmac::HmacInit(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
- Hmac () : ObjectWrap () {
- initialised_ = false;
- }
+ Hmac* hmac = WeakObject::Unwrap<Hmac>(args.This());
- ~Hmac () {
- if (initialised_) {
- HMAC_CTX_cleanup(&ctx);
- }
+ if (args.Length() < 2 || !args[0]->IsString()) {
+ return ThrowError("Must give hashtype string, key as arguments");
}
- private:
+ ASSERT_IS_BUFFER(args[1]);
- HMAC_CTX ctx; /* coverity[member_decl] */
- const EVP_MD *md; /* coverity[member_decl] */
- bool initialised_;
-};
+ const String::Utf8Value hash_type(args[0]);
+ const char* buffer_data = Buffer::Data(args[1]);
+ size_t buffer_length = Buffer::Length(args[1]);
+ hmac->HmacInit(*hash_type, buffer_data, buffer_length);
+}
-class Hash : public ObjectWrap {
- public:
- static void Initialize (v8::Handle<v8::Object> target) {
- HandleScope scope;
+bool Hmac::HmacUpdate(const char* data, int len) {
+ if (!initialised_)
+ return false;
+ HMAC_Update(&ctx_, reinterpret_cast<const unsigned char*>(data), len);
+ return true;
+}
- Local<FunctionTemplate> t = FunctionTemplate::New(New);
- t->InstanceTemplate()->SetInternalFieldCount(1);
+void Hmac::HmacUpdate(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
- NODE_SET_PROTOTYPE_METHOD(t, "update", HashUpdate);
- NODE_SET_PROTOTYPE_METHOD(t, "digest", HashDigest);
+ Hmac* hmac = WeakObject::Unwrap<Hmac>(args.This());
- target->Set(String::NewSymbol("Hash"), t->GetFunction());
- }
+ ASSERT_IS_STRING_OR_BUFFER(args[0]);
- bool HashInit (const char* hashType) {
- md = EVP_get_digestbyname(hashType);
- if(!md) return false;
- EVP_MD_CTX_init(&mdctx);
- EVP_DigestInit_ex(&mdctx, md, NULL);
- initialised_ = true;
- return true;
+ // Only copy the data if we have to, because it's a string
+ bool r;
+ if (args[0]->IsString()) {
+ Local<String> string = args[0].As<String>();
+ enum encoding encoding = ParseEncoding(args[1], BINARY);
+ if (!StringBytes::IsValidString(string, encoding))
+ return ThrowTypeError("Bad input string");
+ size_t buflen = StringBytes::StorageSize(string, encoding);
+ char* buf = new char[buflen];
+ size_t written = StringBytes::Write(buf, buflen, string, encoding);
+ r = hmac->HmacUpdate(buf, written);
+ delete[] buf;
+ } else {
+ char* buf = Buffer::Data(args[0]);
+ size_t buflen = Buffer::Length(args[0]);
+ r = hmac->HmacUpdate(buf, buflen);
}
- int HashUpdate(char* data, int len) {
- if (!initialised_) return 0;
- EVP_DigestUpdate(&mdctx, data, len);
- return 1;
+ if (!r) {
+ return ThrowTypeError("HmacUpdate fail");
}
+}
- protected:
-
- static Handle<Value> New (const Arguments& args) {
- HandleScope scope;
+bool Hmac::HmacDigest(unsigned char** md_value, unsigned int* md_len) {
+ if (!initialised_)
+ return false;
+ *md_value = new unsigned char[EVP_MAX_MD_SIZE];
+ HMAC_Final(&ctx_, *md_value, md_len);
+ HMAC_CTX_cleanup(&ctx_);
+ initialised_ = false;
+ return true;
+}
- if (args.Length() == 0 || !args[0]->IsString()) {
- return ThrowException(Exception::Error(String::New(
- "Must give hashtype string as argument")));
- }
- String::Utf8Value hashType(args[0]);
+void Hmac::HmacDigest(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
- Hash *hash = new Hash();
- if (!hash->HashInit(*hashType)) {
- delete hash;
- return ThrowException(Exception::Error(String::New(
- "Digest method not supported")));
- }
+ Hmac* hmac = WeakObject::Unwrap<Hmac>(args.This());
- hash->Wrap(args.This());
- return args.This();
+ enum encoding encoding = BUFFER;
+ if (args.Length() >= 1) {
+ encoding = ParseEncoding(args[0]->ToString(), BUFFER);
}
- static Handle<Value> HashUpdate(const Arguments& args) {
- HandleScope scope;
-
- Hash *hash = ObjectWrap::Unwrap<Hash>(args.This());
+ unsigned char* md_value = NULL;
+ unsigned int md_len = 0;
- ASSERT_IS_STRING_OR_BUFFER(args[0]);
-
- // Only copy the data if we have to, because it's a string
- int r;
- if (args[0]->IsString()) {
- Local<String> string = args[0].As<String>();
- enum encoding encoding = ParseEncoding(args[1], BINARY);
- if (!StringBytes::IsValidString(string, encoding))
- return ThrowTypeError("Bad input string");
- size_t buflen = StringBytes::StorageSize(string, encoding);
- char* buf = new char[buflen];
- size_t written = StringBytes::Write(buf, buflen, string, encoding);
- r = hash->HashUpdate(buf, written);
- delete[] buf;
- } else {
- char* buf = Buffer::Data(args[0]);
- size_t buflen = Buffer::Length(args[0]);
- r = hash->HashUpdate(buf, buflen);
- }
+ bool r = hmac->HmacDigest(&md_value, &md_len);
+ if (!r) {
+ md_value = NULL;
+ md_len = 0;
+ }
- if (!r) {
- Local<Value> exception = Exception::TypeError(String::New("HashUpdate fail"));
- return ThrowException(exception);
- }
+ Local<Value> rc = StringBytes::Encode(
+ reinterpret_cast<const char*>(md_value), md_len, encoding);
+ delete[] md_value;
+ args.GetReturnValue().Set(rc);
+}
- return args.This();
- }
- static Handle<Value> HashDigest(const Arguments& args) {
- HandleScope scope;
+void Hash::Initialize(Environment* env, v8::Handle<v8::Object> target) {
+ Local<FunctionTemplate> t = FunctionTemplate::New(New);
- Hash *hash = ObjectWrap::Unwrap<Hash>(args.This());
+ t->InstanceTemplate()->SetInternalFieldCount(1);
- if (!hash->initialised_) {
- return ThrowException(Exception::Error(String::New("Not initialized")));
- }
+ NODE_SET_PROTOTYPE_METHOD(t, "update", HashUpdate);
+ NODE_SET_PROTOTYPE_METHOD(t, "digest", HashDigest);
- enum encoding encoding = BUFFER;
- if (args.Length() >= 1) {
- encoding = ParseEncoding(args[0]->ToString(), BUFFER);
- }
+ target->Set(FIXED_ONE_BYTE_STRING(node_isolate, "Hash"), t->GetFunction());
+}
- unsigned char md_value[EVP_MAX_MD_SIZE];
- unsigned int md_len;
- EVP_DigestFinal_ex(&hash->mdctx, md_value, &md_len);
- EVP_MD_CTX_cleanup(&hash->mdctx);
- hash->initialised_ = false;
+void Hash::New(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
- return scope.Close(StringBytes::Encode(
- reinterpret_cast<const char*>(md_value), md_len, encoding));
+ if (args.Length() == 0 || !args[0]->IsString()) {
+ return ThrowError("Must give hashtype string as argument");
}
- Hash () : ObjectWrap () {
- initialised_ = false;
- }
+ const String::Utf8Value hash_type(args[0]);
- ~Hash () {
- if (initialised_) {
- EVP_MD_CTX_cleanup(&mdctx);
- }
+ Hash* hash = new Hash(args.GetIsolate(), args.This());
+ if (!hash->HashInit(*hash_type)) {
+ return ThrowError("Digest method not supported");
}
+}
- private:
- EVP_MD_CTX mdctx; /* coverity[member_decl] */
- const EVP_MD *md; /* coverity[member_decl] */
- bool initialised_;
-};
+bool Hash::HashInit(const char* hash_type) {
+ assert(md_ == NULL);
+ md_ = EVP_get_digestbyname(hash_type);
+ if (md_ == NULL)
+ return false;
+ EVP_MD_CTX_init(&mdctx_);
+ EVP_DigestInit_ex(&mdctx_, md_, NULL);
+ initialised_ = true;
+ return true;
+}
-class Sign : public ObjectWrap {
- public:
- static void
- Initialize (v8::Handle<v8::Object> target) {
- HandleScope scope;
- Local<FunctionTemplate> t = FunctionTemplate::New(New);
+bool Hash::HashUpdate(const char* data, int len) {
+ if (!initialised_)
+ return false;
+ EVP_DigestUpdate(&mdctx_, data, len);
+ return true;
+}
- t->InstanceTemplate()->SetInternalFieldCount(1);
- NODE_SET_PROTOTYPE_METHOD(t, "init", SignInit);
- NODE_SET_PROTOTYPE_METHOD(t, "update", SignUpdate);
- NODE_SET_PROTOTYPE_METHOD(t, "sign", SignFinal);
+void Hash::HashUpdate(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
- target->Set(String::NewSymbol("Sign"), t->GetFunction());
- }
+ Hash* hash = WeakObject::Unwrap<Hash>(args.This());
- bool SignInit (const char* signType) {
- md = EVP_get_digestbyname(signType);
- if(!md) {
- printf("Unknown message digest %s\n", signType);
- return false;
- }
- EVP_MD_CTX_init(&mdctx);
- EVP_SignInit_ex(&mdctx, md, NULL);
- initialised_ = true;
- return true;
+ ASSERT_IS_STRING_OR_BUFFER(args[0]);
+ // Only copy the data if we have to, because it's a string
+ bool r;
+ if (args[0]->IsString()) {
+ Local<String> string = args[0].As<String>();
+ enum encoding encoding = ParseEncoding(args[1], BINARY);
+ if (!StringBytes::IsValidString(string, encoding))
+ return ThrowTypeError("Bad input string");
+ size_t buflen = StringBytes::StorageSize(string, encoding);
+ char* buf = new char[buflen];
+ size_t written = StringBytes::Write(buf, buflen, string, encoding);
+ r = hash->HashUpdate(buf, written);
+ delete[] buf;
+ } else {
+ char* buf = Buffer::Data(args[0]);
+ size_t buflen = Buffer::Length(args[0]);
+ r = hash->HashUpdate(buf, buflen);
}
- int SignUpdate(char* data, int len) {
- if (!initialised_) return 0;
- EVP_SignUpdate(&mdctx, data, len);
- return 1;
+ if (!r) {
+ return ThrowTypeError("HashUpdate fail");
}
+}
- int SignFinal(unsigned char** md_value,
- unsigned int *md_len,
- char* key_pem,
- int key_pemLen) {
- if (!initialised_) return 0;
- BIO *bp = NULL;
- EVP_PKEY* pkey;
- bp = BIO_new(BIO_s_mem());
- if(!BIO_write(bp, key_pem, key_pemLen)) return 0;
+void Hash::HashDigest(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
- pkey = PEM_read_bio_PrivateKey( bp, NULL, NULL, NULL );
- if (pkey == NULL) return 0;
+ Hash* hash = WeakObject::Unwrap<Hash>(args.This());
- EVP_SignFinal(&mdctx, *md_value, md_len, pkey);
- EVP_MD_CTX_cleanup(&mdctx);
- initialised_ = false;
- EVP_PKEY_free(pkey);
- BIO_free(bp);
- return 1;
+ if (!hash->initialised_) {
+ return ThrowError("Not initialized");
}
-
- protected:
-
- static Handle<Value> New (const Arguments& args) {
- HandleScope scope;
-
- Sign *sign = new Sign();
- sign->Wrap(args.This());
-
- return args.This();
+ enum encoding encoding = BUFFER;
+ if (args.Length() >= 1) {
+ encoding = ParseEncoding(args[0]->ToString(), BUFFER);
}
- static Handle<Value> SignInit(const Arguments& args) {
- HandleScope scope;
+ unsigned char md_value[EVP_MAX_MD_SIZE];
+ unsigned int md_len;
- Sign *sign = ObjectWrap::Unwrap<Sign>(args.This());
+ EVP_DigestFinal_ex(&hash->mdctx_, md_value, &md_len);
+ EVP_MD_CTX_cleanup(&hash->mdctx_);
+ hash->initialised_ = false;
- if (args.Length() == 0 || !args[0]->IsString()) {
- return ThrowException(Exception::Error(String::New(
- "Must give signtype string as argument")));
- }
+ Local<Value> rc = StringBytes::Encode(
+ reinterpret_cast<const char*>(md_value), md_len, encoding);
+ args.GetReturnValue().Set(rc);
+}
- String::Utf8Value signType(args[0]);
- bool r = sign->SignInit(*signType);
+void Sign::Initialize(Environment* env, v8::Handle<v8::Object> target) {
+ Local<FunctionTemplate> t = FunctionTemplate::New(New);
- if (!r) {
- return ThrowException(Exception::Error(String::New("SignInit error")));
- }
+ t->InstanceTemplate()->SetInternalFieldCount(1);
- return args.This();
- }
+ NODE_SET_PROTOTYPE_METHOD(t, "init", SignInit);
+ NODE_SET_PROTOTYPE_METHOD(t, "update", SignUpdate);
+ NODE_SET_PROTOTYPE_METHOD(t, "sign", SignFinal);
- static Handle<Value> SignUpdate(const Arguments& args) {
- Sign *sign = ObjectWrap::Unwrap<Sign>(args.This());
+ target->Set(FIXED_ONE_BYTE_STRING(node_isolate, "Sign"), t->GetFunction());
+}
- HandleScope scope;
- ASSERT_IS_STRING_OR_BUFFER(args[0]);
+void Sign::New(const FunctionCallbackInfo<Value>& args) {
+ new Sign(args.GetIsolate(), args.This());
+}
- // Only copy the data if we have to, because it's a string
- int r;
- if (args[0]->IsString()) {
- Local<String> string = args[0].As<String>();
- enum encoding encoding = ParseEncoding(args[1], BINARY);
- if (!StringBytes::IsValidString(string, encoding))
- return ThrowTypeError("Bad input string");
- size_t buflen = StringBytes::StorageSize(string, encoding);
- char* buf = new char[buflen];
- size_t written = StringBytes::Write(buf, buflen, string, encoding);
- r = sign->SignUpdate(buf, written);
- delete[] buf;
- } else {
- char* buf = Buffer::Data(args[0]);
- size_t buflen = Buffer::Length(args[0]);
- r = sign->SignUpdate(buf, buflen);
- }
- if (!r) {
- Local<Value> exception = Exception::TypeError(String::New("SignUpdate fail"));
- return ThrowException(exception);
- }
+void Sign::SignInit(const char* sign_type) {
+ HandleScope scope(node_isolate);
- return args.This();
+ assert(md_ == NULL);
+ md_ = EVP_get_digestbyname(sign_type);
+ if (!md_) {
+ return ThrowError("Uknown message digest");
}
+ EVP_MD_CTX_init(&mdctx_);
+ EVP_SignInit_ex(&mdctx_, md_, NULL);
+ initialised_ = true;
+}
- static Handle<Value> SignFinal(const Arguments& args) {
- Sign *sign = ObjectWrap::Unwrap<Sign>(args.This());
- HandleScope scope;
+void Sign::SignInit(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
- unsigned char* md_value;
- unsigned int md_len;
- Local<Value> outString;
+ Sign* sign = WeakObject::Unwrap<Sign>(args.This());
- ASSERT_IS_BUFFER(args[0]);
- ssize_t len = Buffer::Length(args[0]);
+ if (args.Length() == 0 || !args[0]->IsString()) {
+ return ThrowError("Must give signtype string as argument");
+ }
- enum encoding encoding = BUFFER;
- if (args.Length() >= 2) {
- encoding = ParseEncoding(args[1]->ToString(), BUFFER);
- }
+ const String::Utf8Value sign_type(args[0]);
+ sign->SignInit(*sign_type);
+}
- char* buf = new char[len];
- ssize_t written = DecodeWrite(buf, len, args[0], BUFFER);
- assert(written == len);
- md_len = 8192; // Maximum key size is 8192 bits
- md_value = new unsigned char[md_len];
+bool Sign::SignUpdate(const char* data, int len) {
+ if (!initialised_)
+ return false;
+ EVP_SignUpdate(&mdctx_, data, len);
+ return true;
+}
- int r = sign->SignFinal(&md_value, &md_len, buf, len);
- if (r == 0) {
- md_value = NULL;
- md_len = r;
- }
- delete [] buf;
+void Sign::SignUpdate(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
- outString = StringBytes::Encode(
- reinterpret_cast<const char*>(md_value), md_len, encoding);
+ Sign* sign = WeakObject::Unwrap<Sign>(args.This());
- delete [] md_value;
- return scope.Close(outString);
- }
+ ASSERT_IS_STRING_OR_BUFFER(args[0]);
- Sign () : ObjectWrap () {
- initialised_ = false;
+ // Only copy the data if we have to, because it's a string
+ int r;
+ if (args[0]->IsString()) {
+ Local<String> string = args[0].As<String>();
+ enum encoding encoding = ParseEncoding(args[1], BINARY);
+ if (!StringBytes::IsValidString(string, encoding))
+ return ThrowTypeError("Bad input string");
+ size_t buflen = StringBytes::StorageSize(string, encoding);
+ char* buf = new char[buflen];
+ size_t written = StringBytes::Write(buf, buflen, string, encoding);
+ r = sign->SignUpdate(buf, written);
+ delete[] buf;
+ } else {
+ char* buf = Buffer::Data(args[0]);
+ size_t buflen = Buffer::Length(args[0]);
+ r = sign->SignUpdate(buf, buflen);
}
- ~Sign () {
- if (initialised_) {
- EVP_MD_CTX_cleanup(&mdctx);
- }
+ if (!r) {
+ return ThrowTypeError("SignUpdate fail");
}
+}
- private:
- EVP_MD_CTX mdctx; /* coverity[member_decl] */
- const EVP_MD *md; /* coverity[member_decl] */
- bool initialised_;
-};
+bool Sign::SignFinal(unsigned char** md_value,
+ unsigned int *md_len,
+ const char* key_pem,
+ int key_pem_len) {
+ if (!initialised_)
+ return false;
+
+ BIO* bp = NULL;
+ EVP_PKEY* pkey = NULL;
+ bp = BIO_new(BIO_s_mem());
+ if (!BIO_write(bp, key_pem, key_pem_len))
+ return false;
+
+ pkey = PEM_read_bio_PrivateKey(bp, NULL, NULL, NULL);
+ if (pkey == NULL)
+ return 0;
+
+ EVP_SignFinal(&mdctx_, *md_value, md_len, pkey);
+ EVP_MD_CTX_cleanup(&mdctx_);
+ initialised_ = false;
+ EVP_PKEY_free(pkey);
+ BIO_free_all(bp);
+ return true;
+}
-class Verify : public ObjectWrap {
- public:
- static void Initialize (v8::Handle<v8::Object> target) {
- HandleScope scope;
- Local<FunctionTemplate> t = FunctionTemplate::New(New);
+void Sign::SignFinal(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
- t->InstanceTemplate()->SetInternalFieldCount(1);
+ Sign* sign = WeakObject::Unwrap<Sign>(args.This());
- NODE_SET_PROTOTYPE_METHOD(t, "init", VerifyInit);
- NODE_SET_PROTOTYPE_METHOD(t, "update", VerifyUpdate);
- NODE_SET_PROTOTYPE_METHOD(t, "verify", VerifyFinal);
+ unsigned char* md_value;
+ unsigned int md_len;
- target->Set(String::NewSymbol("Verify"), t->GetFunction());
+ enum encoding encoding = BUFFER;
+ if (args.Length() >= 2) {
+ encoding = ParseEncoding(args[1]->ToString(), BUFFER);
}
+ ASSERT_IS_BUFFER(args[0]);
+ ssize_t len = Buffer::Length(args[0]);
+ char* buf = Buffer::Data(args[0]);
+
+ md_len = 8192; // Maximum key size is 8192 bits
+ md_value = new unsigned char[md_len];
- bool VerifyInit (const char* verifyType) {
- md = EVP_get_digestbyname(verifyType);
- if(!md) {
- fprintf(stderr, "node-crypto : Unknown message digest %s\n", verifyType);
- return false;
- }
- EVP_MD_CTX_init(&mdctx);
- EVP_VerifyInit_ex(&mdctx, md, NULL);
- initialised_ = true;
- return true;
+ bool r = sign->SignFinal(&md_value, &md_len, buf, len);
+ if (!r) {
+ delete[] md_value;
+ md_value = NULL;
+ md_len = 0;
}
-
- int VerifyUpdate(char* data, int len) {
- if (!initialised_) return 0;
- EVP_VerifyUpdate(&mdctx, data, len);
- return 1;
- }
+ Local<Value> rc = StringBytes::Encode(
+ reinterpret_cast<const char*>(md_value), md_len, encoding);
+ delete[] md_value;
+ args.GetReturnValue().Set(rc);
+}
- int VerifyFinal(char* key_pem, int key_pemLen, unsigned char* sig, int siglen) {
- if (!initialised_) return 0;
+void Verify::Initialize(Environment* env, v8::Handle<v8::Object> target) {
+ Local<FunctionTemplate> t = FunctionTemplate::New(New);
- ClearErrorOnReturn clear_error_on_return;
- (void) &clear_error_on_return; // Silence compiler warning.
+ t->InstanceTemplate()->SetInternalFieldCount(1);
- EVP_PKEY* pkey = NULL;
- BIO *bp = NULL;
- X509 *x509 = NULL;
- int r = 0;
+ NODE_SET_PROTOTYPE_METHOD(t, "init", VerifyInit);
+ NODE_SET_PROTOTYPE_METHOD(t, "update", VerifyUpdate);
+ NODE_SET_PROTOTYPE_METHOD(t, "verify", VerifyFinal);
- bp = BIO_new(BIO_s_mem());
- if (bp == NULL) {
- ERR_print_errors_fp(stderr);
- return 0;
- }
- if(!BIO_write(bp, key_pem, key_pemLen)) {
- ERR_print_errors_fp(stderr);
- return 0;
- }
+ target->Set(FIXED_ONE_BYTE_STRING(node_isolate, "Verify"), t->GetFunction());
+}
- // Check if this is a PKCS#8 or RSA public key before trying as X.509.
- // Split this out into a separate function once we have more than one
- // consumer of public keys.
- if (strncmp(key_pem, PUBLIC_KEY_PFX, PUBLIC_KEY_PFX_LEN) == 0) {
- pkey = PEM_read_bio_PUBKEY(bp, NULL, NULL, NULL);
- if (pkey == NULL) {
- ERR_print_errors_fp(stderr);
- return 0;
- }
- } else if (strncmp(key_pem, PUBRSA_KEY_PFX, PUBRSA_KEY_PFX_LEN) == 0) {
- RSA* rsa = PEM_read_bio_RSAPublicKey(bp, NULL, NULL, NULL);
- if (rsa) {
- pkey = EVP_PKEY_new();
- if (pkey) EVP_PKEY_set1_RSA(pkey, rsa);
- RSA_free(rsa);
- }
- if (pkey == NULL) {
- ERR_print_errors_fp(stderr);
- return 0;
- }
- } else {
- // X.509 fallback
- x509 = PEM_read_bio_X509(bp, NULL, NULL, NULL);
- if (x509 == NULL) {
- ERR_print_errors_fp(stderr);
- return 0;
- }
- pkey = X509_get_pubkey(x509);
- if (pkey == NULL) {
- ERR_print_errors_fp(stderr);
- return 0;
- }
- }
+void Verify::New(const FunctionCallbackInfo<Value>& args) {
+ new Verify(args.GetIsolate(), args.This());
+}
- r = EVP_VerifyFinal(&mdctx, sig, siglen, pkey);
- if(pkey != NULL)
- EVP_PKEY_free (pkey);
- if (x509 != NULL)
- X509_free(x509);
- if (bp != NULL)
- BIO_free(bp);
- EVP_MD_CTX_cleanup(&mdctx);
- initialised_ = false;
+void Verify::VerifyInit(const char* verify_type) {
+ HandleScope scope(node_isolate);
- return r;
+ assert(md_ == NULL);
+ md_ = EVP_get_digestbyname(verify_type);
+ if (md_ == NULL) {
+ return ThrowError("Unknown message digest");
}
+ EVP_MD_CTX_init(&mdctx_);
+ EVP_VerifyInit_ex(&mdctx_, md_, NULL);
+ initialised_ = true;
+}
- protected:
- static Handle<Value> New (const Arguments& args) {
- HandleScope scope;
+void Verify::VerifyInit(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
- Verify *verify = new Verify();
- verify->Wrap(args.This());
+ Verify* verify = WeakObject::Unwrap<Verify>(args.This());
- return args.This();
+ if (args.Length() == 0 || !args[0]->IsString()) {
+ return ThrowError("Must give verifytype string as argument");
}
+ const String::Utf8Value verify_type(args[0]);
+ verify->VerifyInit(*verify_type);
+}
- static Handle<Value> VerifyInit(const Arguments& args) {
- Verify *verify = ObjectWrap::Unwrap<Verify>(args.This());
- HandleScope scope;
+bool Verify::VerifyUpdate(const char* data, int len) {
+ if (!initialised_)
+ return false;
+ EVP_VerifyUpdate(&mdctx_, data, len);
+ return true;
+}
- if (args.Length() == 0 || !args[0]->IsString()) {
- return ThrowException(Exception::Error(String::New(
- "Must give verifytype string as argument")));
- }
- String::Utf8Value verifyType(args[0]);
+void Verify::VerifyUpdate(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
- bool r = verify->VerifyInit(*verifyType);
+ Verify* verify = WeakObject::Unwrap<Verify>(args.This());
- if (!r) {
- return ThrowException(Exception::Error(String::New("VerifyInit error")));
- }
+ ASSERT_IS_STRING_OR_BUFFER(args[0]);
- return args.This();
+ // Only copy the data if we have to, because it's a string
+ bool r;
+ if (args[0]->IsString()) {
+ Local<String> string = args[0].As<String>();
+ enum encoding encoding = ParseEncoding(args[1], BINARY);
+ if (!StringBytes::IsValidString(string, encoding))
+ return ThrowTypeError("Bad input string");
+ size_t buflen = StringBytes::StorageSize(string, encoding);
+ char* buf = new char[buflen];
+ size_t written = StringBytes::Write(buf, buflen, string, encoding);
+ r = verify->VerifyUpdate(buf, written);
+ delete[] buf;
+ } else {
+ char* buf = Buffer::Data(args[0]);
+ size_t buflen = Buffer::Length(args[0]);
+ r = verify->VerifyUpdate(buf, buflen);
}
+ if (!r) {
+ return ThrowTypeError("VerifyUpdate fail");
+ }
+}
- static Handle<Value> VerifyUpdate(const Arguments& args) {
- HandleScope scope;
- Verify *verify = ObjectWrap::Unwrap<Verify>(args.This());
+bool Verify::VerifyFinal(const char* key_pem,
+ int key_pem_len,
+ const char* sig,
+ int siglen) {
+ HandleScope scope(node_isolate);
- ASSERT_IS_STRING_OR_BUFFER(args[0]);
+ if (!initialised_) {
+ ThrowError("Verify not initalised");
+ return false;
+ }
- // Only copy the data if we have to, because it's a string
- int r;
- if (args[0]->IsString()) {
- Local<String> string = args[0].As<String>();
- enum encoding encoding = ParseEncoding(args[1], BINARY);
- if (!StringBytes::IsValidString(string, encoding))
- return ThrowTypeError("Bad input string");
- size_t buflen = StringBytes::StorageSize(string, encoding);
- char* buf = new char[buflen];
- size_t written = StringBytes::Write(buf, buflen, string, encoding);
- r = verify->VerifyUpdate(buf, written);
- delete[] buf;
- } else {
- char* buf = Buffer::Data(args[0]);
- size_t buflen = Buffer::Length(args[0]);
- r = verify->VerifyUpdate(buf, buflen);
- }
++ ClearErrorOnReturn clear_error_on_return;
++ (void) &clear_error_on_return; // Silence compiler warning.
+
- if (!r) {
- Local<Value> exception = Exception::TypeError(String::New("VerifyUpdate fail"));
- return ThrowException(exception);
+ EVP_PKEY* pkey = NULL;
+ BIO* bp = NULL;
+ X509* x509 = NULL;
+ bool fatal = true;
+ int r = 0;
+
+ bp = BIO_new(BIO_s_mem());
+ if (bp == NULL)
+ goto exit;
+
+ if (!BIO_write(bp, key_pem, key_pem_len))
+ goto exit;
+
+ // Check if this is a PKCS#8 or RSA public key before trying as X.509.
+ // Split this out into a separate function once we have more than one
+ // consumer of public keys.
+ if (strncmp(key_pem, PUBLIC_KEY_PFX, PUBLIC_KEY_PFX_LEN) == 0) {
+ pkey = PEM_read_bio_PUBKEY(bp, NULL, NULL, NULL);
+ if (pkey == NULL)
+ goto exit;
+ } else if (strncmp(key_pem, PUBRSA_KEY_PFX, PUBRSA_KEY_PFX_LEN) == 0) {
+ RSA* rsa = PEM_read_bio_RSAPublicKey(bp, NULL, NULL, NULL);
+ if (rsa) {
+ pkey = EVP_PKEY_new();
+ if (pkey)
+ EVP_PKEY_set1_RSA(pkey, rsa);
+ RSA_free(rsa);
}
+ if (pkey == NULL)
+ goto exit;
+ } else {
+ // X.509 fallback
+ x509 = PEM_read_bio_X509(bp, NULL, NULL, NULL);
+ if (x509 == NULL)
+ goto exit;
- return args.This();
+ pkey = X509_get_pubkey(x509);
+ if (pkey == NULL)
+ goto exit;
}
+ fatal = false;
+ r = EVP_VerifyFinal(&mdctx_,
+ reinterpret_cast<const unsigned char*>(sig),
+ siglen,
+ pkey);
- static Handle<Value> VerifyFinal(const Arguments& args) {
- HandleScope scope;
+ exit:
+ if (pkey != NULL)
+ EVP_PKEY_free(pkey);
+ if (bp != NULL)
+ BIO_free_all(bp);
+ if (x509 != NULL)
+ X509_free(x509);
- Verify *verify = ObjectWrap::Unwrap<Verify>(args.This());
+ EVP_MD_CTX_cleanup(&mdctx_);
+ initialised_ = false;
- ASSERT_IS_BUFFER(args[0]);
- ssize_t klen = Buffer::Length(args[0]);
+ if (fatal) {
+ unsigned long err = ERR_get_error();
+ ThrowCryptoError(err);
+ return false;
+ }
- if (klen < 0) {
- Local<Value> exception = Exception::TypeError(String::New("Bad argument"));
- return ThrowException(exception);
- }
+ return r == 1;
+}
- char* kbuf = new char[klen];
- ssize_t kwritten = DecodeWrite(kbuf, klen, args[0], BINARY);
- assert(kwritten == klen);
- ASSERT_IS_STRING_OR_BUFFER(args[1]);
+void Verify::VerifyFinal(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
- // BINARY works for both buffers and binary strings.
- enum encoding encoding = BINARY;
- if (args.Length() >= 3) {
- encoding = ParseEncoding(args[2]->ToString(), BINARY);
- }
+ Verify* verify = WeakObject::Unwrap<Verify>(args.This());
- ssize_t hlen = StringBytes::Size(args[1], encoding);
+ ASSERT_IS_BUFFER(args[0]);
+ char* kbuf = Buffer::Data(args[0]);
+ ssize_t klen = Buffer::Length(args[0]);
- if (hlen < 0) {
- delete[] kbuf;
- Local<Value> exception = Exception::TypeError(String::New("Bad argument"));
- return ThrowException(exception);
- }
+ ASSERT_IS_STRING_OR_BUFFER(args[1]);
+ // BINARY works for both buffers and binary strings.
+ enum encoding encoding = BINARY;
+ if (args.Length() >= 3) {
+ encoding = ParseEncoding(args[2]->ToString(), BINARY);
+ }
- unsigned char* hbuf = new unsigned char[hlen];
- ssize_t hwritten = StringBytes::Write(
- reinterpret_cast<char*>(hbuf), hlen, args[1], encoding);
- assert(hwritten == hlen);
+ ssize_t hlen = StringBytes::Size(args[1], encoding);
- int r;
- r = verify->VerifyFinal(kbuf, klen, hbuf, hlen);
+ // only copy if we need to, because it's a string.
+ char* hbuf;
+ if (args[1]->IsString()) {
+ hbuf = new char[hlen];
+ ssize_t hwritten = StringBytes::Write(hbuf, hlen, args[1], encoding);
+ assert(hwritten == hlen);
+ } else {
+ hbuf = Buffer::Data(args[1]);
+ }
- delete[] kbuf;
+ bool rc = verify->VerifyFinal(kbuf, klen, hbuf, hlen);
+ if (args[1]->IsString()) {
delete[] hbuf;
-
- return Boolean::New(r && r != -1);
}
+ args.GetReturnValue().Set(rc);
+}
- Verify () : ObjectWrap () {
- initialised_ = false;
- }
- ~Verify () {
- if (initialised_) {
- EVP_MD_CTX_cleanup(&mdctx);
- }
- }
+void DiffieHellman::Initialize(Environment* env, Handle<Object> target) {
+ Local<FunctionTemplate> t = FunctionTemplate::New(New);
- private:
+ t->InstanceTemplate()->SetInternalFieldCount(1);
- EVP_MD_CTX mdctx; /* coverity[member_decl] */
- const EVP_MD *md; /* coverity[member_decl] */
- bool initialised_;
+ NODE_SET_PROTOTYPE_METHOD(t, "generateKeys", GenerateKeys);
+ NODE_SET_PROTOTYPE_METHOD(t, "computeSecret", ComputeSecret);
+ NODE_SET_PROTOTYPE_METHOD(t, "getPrime", GetPrime);
+ NODE_SET_PROTOTYPE_METHOD(t, "getGenerator", GetGenerator);
+ NODE_SET_PROTOTYPE_METHOD(t, "getPublicKey", GetPublicKey);
+ NODE_SET_PROTOTYPE_METHOD(t, "getPrivateKey", GetPrivateKey);
+ NODE_SET_PROTOTYPE_METHOD(t, "setPublicKey", SetPublicKey);
+ NODE_SET_PROTOTYPE_METHOD(t, "setPrivateKey", SetPrivateKey);
-};
+ target->Set(FIXED_ONE_BYTE_STRING(node_isolate, "DiffieHellman"),
+ t->GetFunction());
-class DiffieHellman : public ObjectWrap {
- public:
- static void Initialize(v8::Handle<v8::Object> target) {
- HandleScope scope;
+ Local<FunctionTemplate> t2 = FunctionTemplate::New(DiffieHellmanGroup);
+ t2->InstanceTemplate()->SetInternalFieldCount(1);
- Local<FunctionTemplate> t = FunctionTemplate::New(New);
+ NODE_SET_PROTOTYPE_METHOD(t2, "generateKeys", GenerateKeys);
+ NODE_SET_PROTOTYPE_METHOD(t2, "computeSecret", ComputeSecret);
+ NODE_SET_PROTOTYPE_METHOD(t2, "getPrime", GetPrime);
+ NODE_SET_PROTOTYPE_METHOD(t2, "getGenerator", GetGenerator);
+ NODE_SET_PROTOTYPE_METHOD(t2, "getPublicKey", GetPublicKey);
+ NODE_SET_PROTOTYPE_METHOD(t2, "getPrivateKey", GetPrivateKey);
- t->InstanceTemplate()->SetInternalFieldCount(1);
+ target->Set(FIXED_ONE_BYTE_STRING(node_isolate, "DiffieHellmanGroup"),
+ t2->GetFunction());
+}
- NODE_SET_PROTOTYPE_METHOD(t, "generateKeys", GenerateKeys);
- NODE_SET_PROTOTYPE_METHOD(t, "computeSecret", ComputeSecret);
- NODE_SET_PROTOTYPE_METHOD(t, "getPrime", GetPrime);
- NODE_SET_PROTOTYPE_METHOD(t, "getGenerator", GetGenerator);
- NODE_SET_PROTOTYPE_METHOD(t, "getPublicKey", GetPublicKey);
- NODE_SET_PROTOTYPE_METHOD(t, "getPrivateKey", GetPrivateKey);
- NODE_SET_PROTOTYPE_METHOD(t, "setPublicKey", SetPublicKey);
- NODE_SET_PROTOTYPE_METHOD(t, "setPrivateKey", SetPrivateKey);
- target->Set(String::NewSymbol("DiffieHellman"), t->GetFunction());
+bool DiffieHellman::Init(int primeLength) {
+ dh = DH_new();
+ DH_generate_parameters_ex(dh, primeLength, DH_GENERATOR_2, 0);
+ bool result = VerifyContext();
+ if (!result)
+ return false;
+ initialised_ = true;
+ return true;
+}
- Local<FunctionTemplate> t2 = FunctionTemplate::New(DiffieHellmanGroup);
- t2->InstanceTemplate()->SetInternalFieldCount(1);
- NODE_SET_PROTOTYPE_METHOD(t2, "generateKeys", GenerateKeys);
- NODE_SET_PROTOTYPE_METHOD(t2, "computeSecret", ComputeSecret);
- NODE_SET_PROTOTYPE_METHOD(t2, "getPrime", GetPrime);
- NODE_SET_PROTOTYPE_METHOD(t2, "getGenerator", GetGenerator);
- NODE_SET_PROTOTYPE_METHOD(t2, "getPublicKey", GetPublicKey);
- NODE_SET_PROTOTYPE_METHOD(t2, "getPrivateKey", GetPrivateKey);
+bool DiffieHellman::Init(const char* p, int p_len) {
+ dh = DH_new();
+ dh->p = BN_bin2bn(reinterpret_cast<const unsigned char*>(p), p_len, 0);
+ dh->g = BN_new();
+ if (!BN_set_word(dh->g, 2))
+ return false;
+ bool result = VerifyContext();
+ if (!result)
+ return false;
+ initialised_ = true;
+ return true;
+}
- target->Set(String::NewSymbol("DiffieHellmanGroup"), t2->GetFunction());
- }
- bool Init(int primeLength) {
- dh = DH_new();
- DH_generate_parameters_ex(dh, primeLength, DH_GENERATOR_2, 0);
- bool result = VerifyContext();
- if (!result) return false;
- initialised_ = true;
- return true;
- }
+bool DiffieHellman::Init(const char* p, int p_len, const char* g, int g_len) {
+ dh = DH_new();
+ dh->p = BN_bin2bn(reinterpret_cast<const unsigned char*>(p), p_len, 0);
+ dh->g = BN_bin2bn(reinterpret_cast<const unsigned char*>(g), g_len, 0);
+ initialised_ = true;
+ return true;
+}
- bool Init(unsigned char* p, int p_len) {
- dh = DH_new();
- dh->p = BN_bin2bn(p, p_len, 0);
- dh->g = BN_new();
- if (!BN_set_word(dh->g, 2)) return false;
- bool result = VerifyContext();
- if (!result) return false;
- initialised_ = true;
- return true;
- }
- bool Init(unsigned char* p, int p_len, unsigned char* g, int g_len) {
- dh = DH_new();
- dh->p = BN_bin2bn(p, p_len, 0);
- dh->g = BN_bin2bn(g, g_len, 0);
- initialised_ = true;
- return true;
+void DiffieHellman::DiffieHellmanGroup(
+ const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
+
+ DiffieHellman* diffieHellman =
+ new DiffieHellman(args.GetIsolate(), args.This());
+
+ if (args.Length() != 1 || !args[0]->IsString()) {
+ return ThrowError("No group name given");
}
- protected:
- static Handle<Value> DiffieHellmanGroup(const Arguments& args) {
- HandleScope scope;
+ const String::Utf8Value group_name(args[0]);
+ for (unsigned int i = 0; i < ARRAY_SIZE(modp_groups); ++i) {
+ const modp_group* it = modp_groups + i;
- DiffieHellman* diffieHellman = new DiffieHellman();
+ if (strcasecmp(*group_name, it->name) != 0)
+ continue;
- if (args.Length() != 1 || !args[0]->IsString()) {
- return ThrowException(Exception::Error(
- String::New("No group name given")));
- }
+ diffieHellman->Init(it->prime,
+ it->prime_size,
+ it->gen,
+ it->gen_size);
+ return;
+ }
- String::Utf8Value group_name(args[0]);
+ ThrowError("Unknown group");
+}
- modp_group* it = modp_groups;
- while(it->name != NULL) {
- if (!strcasecmp(*group_name, it->name))
- break;
- it++;
- }
+void DiffieHellman::New(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
- if (it->name != NULL) {
- diffieHellman->Init(it->prime, it->prime_size,
- it->gen, it->gen_size);
+ DiffieHellman* diffieHellman =
+ new DiffieHellman(args.GetIsolate(), args.This());
+ bool initialized = false;
+
+ if (args.Length() > 0) {
+ if (args[0]->IsInt32()) {
+ initialized = diffieHellman->Init(args[0]->Int32Value());
} else {
- return ThrowException(Exception::Error(
- String::New("Unknown group")));
+ initialized = diffieHellman->Init(Buffer::Data(args[0]),
+ Buffer::Length(args[0]));
}
-
- diffieHellman->Wrap(args.This());
-
- return args.This();
}
- static Handle<Value> New(const Arguments& args) {
- HandleScope scope;
-
- DiffieHellman* diffieHellman = new DiffieHellman();
- bool initialized = false;
+ if (!initialized) {
+ return ThrowError("Initialization failed");
+ }
+}
- if (args.Length() > 0) {
- if (args[0]->IsInt32()) {
- initialized = diffieHellman->Init(args[0]->Int32Value());
- } else {
- initialized = diffieHellman->Init(
- reinterpret_cast<unsigned char*>(Buffer::Data(args[0])),
- Buffer::Length(args[0]));
- }
- }
- if (!initialized) {
- return ThrowException(Exception::Error(
- String::New("Initialization failed")));
- }
+void DiffieHellman::GenerateKeys(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
- diffieHellman->Wrap(args.This());
+ DiffieHellman* diffieHellman =
+ WeakObject::Unwrap<DiffieHellman>(args.This());
- return args.This();
+ if (!diffieHellman->initialised_) {
+ return ThrowError("Not initialized");
}
- static Handle<Value> GenerateKeys(const Arguments& args) {
- DiffieHellman* diffieHellman =
- ObjectWrap::Unwrap<DiffieHellman>(args.This());
-
- HandleScope scope;
+ if (!DH_generate_key(diffieHellman->dh)) {
+ return ThrowError("Key generation failed");
+ }
- if (!diffieHellman->initialised_) {
- return ThrowException(Exception::Error(
- String::New("Not initialized")));
- }
+ int dataSize = BN_num_bytes(diffieHellman->dh->pub_key);
+ char* data = new char[dataSize];
+ BN_bn2bin(diffieHellman->dh->pub_key,
+ reinterpret_cast<unsigned char*>(data));
- if (!DH_generate_key(diffieHellman->dh)) {
- return ThrowException(Exception::Error(
- String::New("Key generation failed")));
- }
+ args.GetReturnValue().Set(Encode(data, dataSize, BUFFER));
+ delete[] data;
+}
- Local<Value> outString;
- int dataSize = BN_num_bytes(diffieHellman->dh->pub_key);
- char* data = new char[dataSize];
- BN_bn2bin(diffieHellman->dh->pub_key,
- reinterpret_cast<unsigned char*>(data));
+void DiffieHellman::GetPrime(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
- outString = Encode(data, dataSize, BUFFER);
- delete[] data;
+ DiffieHellman* diffieHellman =
+ WeakObject::Unwrap<DiffieHellman>(args.This());
- return scope.Close(outString);
+ if (!diffieHellman->initialised_) {
+ return ThrowError("Not initialized");
}
- static Handle<Value> GetPrime(const Arguments& args) {
- DiffieHellman* diffieHellman =
- ObjectWrap::Unwrap<DiffieHellman>(args.This());
-
- HandleScope scope;
+ int dataSize = BN_num_bytes(diffieHellman->dh->p);
+ char* data = new char[dataSize];
+ BN_bn2bin(diffieHellman->dh->p, reinterpret_cast<unsigned char*>(data));
- if (!diffieHellman->initialised_) {
- return ThrowException(Exception::Error(String::New("Not initialized")));
- }
-
- int dataSize = BN_num_bytes(diffieHellman->dh->p);
- char* data = new char[dataSize];
- BN_bn2bin(diffieHellman->dh->p, reinterpret_cast<unsigned char*>(data));
+ args.GetReturnValue().Set(Encode(data, dataSize, BUFFER));
+ delete[] data;
+}
- Local<Value> outString;
- outString = Encode(data, dataSize, BUFFER);
+void DiffieHellman::GetGenerator(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
- delete[] data;
+ DiffieHellman* diffieHellman =
+ WeakObject::Unwrap<DiffieHellman>(args.This());
- return scope.Close(outString);
+ if (!diffieHellman->initialised_) {
+ return ThrowError("Not initialized");
}
- static Handle<Value> GetGenerator(const Arguments& args) {
- DiffieHellman* diffieHellman =
- ObjectWrap::Unwrap<DiffieHellman>(args.This());
-
- HandleScope scope;
-
- if (!diffieHellman->initialised_) {
- return ThrowException(Exception::Error(String::New("Not initialized")));
- }
+ int dataSize = BN_num_bytes(diffieHellman->dh->g);
+ char* data = new char[dataSize];
+ BN_bn2bin(diffieHellman->dh->g, reinterpret_cast<unsigned char*>(data));
- int dataSize = BN_num_bytes(diffieHellman->dh->g);
- char* data = new char[dataSize];
- BN_bn2bin(diffieHellman->dh->g, reinterpret_cast<unsigned char*>(data));
+ args.GetReturnValue().Set(Encode(data, dataSize, BUFFER));
+ delete[] data;
+}
- Local<Value> outString;
- outString = Encode(data, dataSize, BUFFER);
+void DiffieHellman::GetPublicKey(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
- delete[] data;
+ DiffieHellman* diffieHellman =
+ WeakObject::Unwrap<DiffieHellman>(args.This());
- return scope.Close(outString);
+ if (!diffieHellman->initialised_) {
+ return ThrowError("Not initialized");
}
- static Handle<Value> GetPublicKey(const Arguments& args) {
- DiffieHellman* diffieHellman =
- ObjectWrap::Unwrap<DiffieHellman>(args.This());
-
- HandleScope scope;
-
- if (!diffieHellman->initialised_) {
- return ThrowException(Exception::Error(String::New("Not initialized")));
- }
+ if (diffieHellman->dh->pub_key == NULL) {
+ return ThrowError("No public key - did you forget to generate one?");
+ }
- if (diffieHellman->dh->pub_key == NULL) {
- return ThrowException(Exception::Error(
- String::New("No public key - did you forget to generate one?")));
- }
+ int dataSize = BN_num_bytes(diffieHellman->dh->pub_key);
+ char* data = new char[dataSize];
+ BN_bn2bin(diffieHellman->dh->pub_key,
+ reinterpret_cast<unsigned char*>(data));
- int dataSize = BN_num_bytes(diffieHellman->dh->pub_key);
- char* data = new char[dataSize];
- BN_bn2bin(diffieHellman->dh->pub_key,
- reinterpret_cast<unsigned char*>(data));
+ args.GetReturnValue().Set(Encode(data, dataSize, BUFFER));
+ delete[] data;
+}
- Local<Value> outString;
- outString = Encode(data, dataSize, BUFFER);
+void DiffieHellman::GetPrivateKey(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
- delete[] data;
+ DiffieHellman* diffieHellman =
+ WeakObject::Unwrap<DiffieHellman>(args.This());
- return scope.Close(outString);
+ if (!diffieHellman->initialised_) {
+ return ThrowError("Not initialized");
}
- static Handle<Value> GetPrivateKey(const Arguments& args) {
- DiffieHellman* diffieHellman =
- ObjectWrap::Unwrap<DiffieHellman>(args.This());
+ if (diffieHellman->dh->priv_key == NULL) {
+ return ThrowError("No private key - did you forget to generate one?");
+ }
- HandleScope scope;
+ int dataSize = BN_num_bytes(diffieHellman->dh->priv_key);
+ char* data = new char[dataSize];
+ BN_bn2bin(diffieHellman->dh->priv_key,
+ reinterpret_cast<unsigned char*>(data));
- if (!diffieHellman->initialised_) {
- return ThrowException(Exception::Error(String::New("Not initialized")));
- }
+ args.GetReturnValue().Set(Encode(data, dataSize, BUFFER));
+ delete[] data;
+}
- if (diffieHellman->dh->priv_key == NULL) {
- return ThrowException(Exception::Error(
- String::New("No private key - did you forget to generate one?")));
- }
- int dataSize = BN_num_bytes(diffieHellman->dh->priv_key);
- char* data = new char[dataSize];
- BN_bn2bin(diffieHellman->dh->priv_key,
- reinterpret_cast<unsigned char*>(data));
+void DiffieHellman::ComputeSecret(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
- Local<Value> outString;
+ DiffieHellman* diffieHellman =
+ WeakObject::Unwrap<DiffieHellman>(args.This());
- outString = Encode(data, dataSize, BUFFER);
+ if (!diffieHellman->initialised_) {
+ return ThrowError("Not initialized");
+ }
- delete[] data;
+ ClearErrorOnReturn clear_error_on_return;
+ (void) &clear_error_on_return; // Silence compiler warning.
+ BIGNUM* key = NULL;
- return scope.Close(outString);
+ if (args.Length() == 0) {
+ return ThrowError("First argument must be other party's public key");
+ } else {
+ ASSERT_IS_BUFFER(args[0]);
+ key = BN_bin2bn(
+ reinterpret_cast<unsigned char*>(Buffer::Data(args[0])),
+ Buffer::Length(args[0]),
+ 0);
}
- static Handle<Value> ComputeSecret(const Arguments& args) {
- HandleScope scope;
+ int dataSize = DH_size(diffieHellman->dh);
+ char* data = new char[dataSize];
- DiffieHellman* diffieHellman =
- ObjectWrap::Unwrap<DiffieHellman>(args.This());
-
- if (!diffieHellman->initialised_) {
- return ThrowException(Exception::Error(String::New("Not initialized")));
- }
+ int size = DH_compute_key(reinterpret_cast<unsigned char*>(data),
+ key,
+ diffieHellman->dh);
- ClearErrorOnReturn clear_error_on_return;
- (void) &clear_error_on_return; // Silence compiler warning.
- BIGNUM* key = 0;
+ if (size == -1) {
+ int checkResult;
+ int checked;
- if (args.Length() == 0) {
- return ThrowException(Exception::Error(
- String::New("First argument must be other party's public key")));
- } else {
- ASSERT_IS_BUFFER(args[0]);
- key = BN_bin2bn(
- reinterpret_cast<unsigned char*>(Buffer::Data(args[0])),
- Buffer::Length(args[0]), 0);
- }
+ checked = DH_check_pub_key(diffieHellman->dh, key, &checkResult);
+ BN_free(key);
+ delete[] data;
- int dataSize = DH_size(diffieHellman->dh);
- char* data = new char[dataSize];
-
- int size = DH_compute_key(reinterpret_cast<unsigned char*>(data),
- key, diffieHellman->dh);
-
- if (size == -1) {
- int checkResult;
- int checked;
-
- checked = DH_check_pub_key(diffieHellman->dh, key, &checkResult);
- BN_free(key);
- delete[] data;
-
- if (!checked) {
- return ThrowException(Exception::Error(String::New("Invalid key")));
- } else if (checkResult) {
- if (checkResult & DH_CHECK_PUBKEY_TOO_SMALL) {
- return ThrowException(Exception::Error(
- String::New("Supplied key is too small")));
- } else if (checkResult & DH_CHECK_PUBKEY_TOO_LARGE) {
- return ThrowException(Exception::Error(
- String::New("Supplied key is too large")));
- } else {
- return ThrowException(Exception::Error(String::New("Invalid key")));
- }
+ if (!checked) {
+ return ThrowError("Invalid key");
+ } else if (checkResult) {
+ if (checkResult & DH_CHECK_PUBKEY_TOO_SMALL) {
+ return ThrowError("Supplied key is too small");
+ } else if (checkResult & DH_CHECK_PUBKEY_TOO_LARGE) {
+ return ThrowError("Supplied key is too large");
} else {
- return ThrowException(Exception::Error(String::New("Invalid key")));
+ return ThrowError("Invalid key");
}
+ } else {
+ return ThrowError("Invalid key");
}
+ }
- BN_free(key);
- assert(size >= 0);
-
- // DH_size returns number of bytes in a prime number
- // DH_compute_key returns number of bytes in a remainder of exponent, which
- // may have less bytes than a prime number. Therefore add 0-padding to the
- // allocated buffer.
- if (size != dataSize) {
- assert(dataSize > size);
- memmove(data + dataSize - size, data, size);
- memset(data, 0, dataSize - size);
- }
-
- Local<Value> outString;
-
- outString = Encode(data, dataSize, BUFFER);
+ BN_free(key);
+ assert(size >= 0);
- delete[] data;
- return scope.Close(outString);
+ // DH_size returns number of bytes in a prime number
+ // DH_compute_key returns number of bytes in a remainder of exponent, which
+ // may have less bytes than a prime number. Therefore add 0-padding to the
+ // allocated buffer.
+ if (size != dataSize) {
+ assert(dataSize > size);
+ memmove(data + dataSize - size, data, size);
+ memset(data, 0, dataSize - size);
}
- static Handle<Value> SetPublicKey(const Arguments& args) {
- HandleScope scope;
+ args.GetReturnValue().Set(Encode(data, dataSize, BUFFER));
+ delete[] data;
+}
- DiffieHellman* diffieHellman =
- ObjectWrap::Unwrap<DiffieHellman>(args.This());
- if (!diffieHellman->initialised_) {
- return ThrowException(Exception::Error(String::New("Not initialized")));
- }
+void DiffieHellman::SetPublicKey(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
- if (args.Length() == 0) {
- return ThrowException(Exception::Error(
- String::New("First argument must be public key")));
- } else {
- ASSERT_IS_BUFFER(args[0]);
- diffieHellman->dh->pub_key =
- BN_bin2bn(
- reinterpret_cast<unsigned char*>(Buffer::Data(args[0])),
- Buffer::Length(args[0]), 0);
- }
+ DiffieHellman* diffieHellman =
+ WeakObject::Unwrap<DiffieHellman>(args.This());
- return args.This();
+ if (!diffieHellman->initialised_) {
+ return ThrowError("Not initialized");
}
- static Handle<Value> SetPrivateKey(const Arguments& args) {
- HandleScope scope;
-
- DiffieHellman* diffieHellman =
- ObjectWrap::Unwrap<DiffieHellman>(args.This());
+ if (args.Length() == 0) {
+ return ThrowError("First argument must be public key");
+ } else {
+ ASSERT_IS_BUFFER(args[0]);
+ diffieHellman->dh->pub_key = BN_bin2bn(
+ reinterpret_cast<unsigned char*>(Buffer::Data(args[0])),
+ Buffer::Length(args[0]), 0);
+ }
+}
- if (!diffieHellman->initialised_) {
- return ThrowException(Exception::Error(
- String::New("Not initialized")));
- }
- if (args.Length() == 0) {
- return ThrowException(Exception::Error(
- String::New("First argument must be private key")));
- } else {
- ASSERT_IS_BUFFER(args[0]);
- diffieHellman->dh->priv_key =
- BN_bin2bn(
- reinterpret_cast<unsigned char*>(Buffer::Data(args[0])),
- Buffer::Length(args[0]), 0);
- }
+void DiffieHellman::SetPrivateKey(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
- return args.This();
- }
+ DiffieHellman* diffieHellman =
+ WeakObject::Unwrap<DiffieHellman>(args.This());
- DiffieHellman() : ObjectWrap() {
- initialised_ = false;
- dh = NULL;
+ if (!diffieHellman->initialised_) {
+ return ThrowError("Not initialized");
}
- ~DiffieHellman() {
- if (dh != NULL) {
- DH_free(dh);
- }
+ if (args.Length() == 0) {
+ return ThrowError("First argument must be private key");
+ } else {
+ ASSERT_IS_BUFFER(args[0]);
+ diffieHellman->dh->priv_key = BN_bin2bn(
+ reinterpret_cast<unsigned char*>(Buffer::Data(args[0])),
+ Buffer::Length(args[0]),
+ 0);
}
+}
- private:
- bool VerifyContext() {
- int codes;
- if (!DH_check(dh, &codes)) return false;
- if (codes & DH_CHECK_P_NOT_SAFE_PRIME) return false;
- if (codes & DH_CHECK_P_NOT_PRIME) return false;
- if (codes & DH_UNABLE_TO_CHECK_GENERATOR) return false;
- if (codes & DH_NOT_SUITABLE_GENERATOR) return false;
- return true;
- }
- bool initialised_;
- DH* dh;
-};
+bool DiffieHellman::VerifyContext() {
+ int codes;
+ if (!DH_check(dh, &codes))
+ return false;
+ if (codes & DH_CHECK_P_NOT_SAFE_PRIME)
+ return false;
+ if (codes & DH_CHECK_P_NOT_PRIME)
+ return false;
+ if (codes & DH_UNABLE_TO_CHECK_GENERATOR)
+ return false;
+ if (codes & DH_NOT_SUITABLE_GENERATOR)
+ return false;
+ return true;
+}
+// TODO(bnoordhuis) Turn into proper RAII class.
struct pbkdf2_req {
uv_work_t work_req;
+ Environment* env;
int err;
char* pass;
size_t passlen;