diff --git a/amt-ider-node-0.0.1.js b/amt-ider-node-0.0.1.js index cfbfbbf..a6ff08d 100644 --- a/amt-ider-node-0.0.1.js +++ b/amt-ider-node-0.0.1.js @@ -443,7 +443,7 @@ var CreateAmtRemoteIderIMR = function () { }); } else { // Open connection with TLS - if (obj.m.xtlsoptions == null) { obj.m.xtlsoptions = { secureProtocol: 'TLSv1_method', ciphers: 'RSA+AES:!aNULL:!MD5:!DSS', secureOptions: obj.constants.SSL_OP_NO_SSLv2 | obj.constants.SSL_OP_NO_SSLv3 | obj.constants.SSL_OP_NO_COMPRESSION | obj.constants.SSL_OP_CIPHER_SERVER_PREFERENCE, rejectUnauthorized: false }; } + if (obj.m.xtlsoptions == null) { obj.m.xtlsoptions = { secureProtocol: 'TLSv1_method', ciphers: 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:RSA+AES:!aNULL:!MD5:!DSS', secureOptions: obj.constants.SSL_OP_NO_SSLv2 | obj.constants.SSL_OP_NO_SSLv3 | obj.constants.SSL_OP_NO_COMPRESSION | obj.constants.SSL_OP_CIPHER_SERVER_PREFERENCE, rejectUnauthorized: false }; } obj.m.client = _tls.connect(obj.m.port, obj.m.host, obj.m.xtlsoptions, function () { //console.log('IDER Connected TLS, ' + obj.m.host + ':' + obj.m.port); diff --git a/amt-redir-node-0.1.0.js b/amt-redir-node-0.1.0.js index 196705c..e5010d5 100644 --- a/amt-redir-node-0.1.0.js +++ b/amt-redir-node-0.1.0.js @@ -64,7 +64,7 @@ var CreateAmtRedirect = function (module) { obj.socket.on('close', obj.xxOnSocketClosed); obj.socket.on('error', obj.xxOnSocketClosed); } else { - if (obj.xtlsoptions == null) { obj.xtlsoptions = { secureProtocol: 'TLSv1_method', ciphers: 'RSA+AES:!aNULL:!MD5:!DSS', secureOptions: obj.constants.SSL_OP_NO_SSLv2 | obj.constants.SSL_OP_NO_SSLv3 | obj.constants.SSL_OP_NO_COMPRESSION | obj.constants.SSL_OP_CIPHER_SERVER_PREFERENCE, rejectUnauthorized: false }; } + if (obj.xtlsoptions == null) { obj.xtlsoptions = { secureProtocol: 'TLSv1_method', ciphers: 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:RSA+AES:!aNULL:!MD5:!DSS', secureOptions: obj.constants.SSL_OP_NO_SSLv2 | obj.constants.SSL_OP_NO_SSLv3 | obj.constants.SSL_OP_NO_COMPRESSION | obj.constants.SSL_OP_CIPHER_SERVER_PREFERENCE, rejectUnauthorized: false }; } obj.socket = obj.tls.connect(port, host, obj.xtlsoptions, obj.xxOnSocketConnected); //obj.socket.setEncoding('binary'); obj.socket.on('data', obj.xxOnSocketData); diff --git a/amt-wsman-node-0.2.0.js b/amt-wsman-node-0.2.0.js index 4eb67c0..732bf65 100644 --- a/amt-wsman-node-0.2.0.js +++ b/amt-wsman-node-0.2.0.js @@ -184,7 +184,7 @@ var CreateWsmanComm = function (host, port, user, pass, tls, tlsoptions) { obj.socket.connect(obj.port, obj.host, obj.xxOnSocketConnected); } else { // Connect with TLS - var options = { secureProtocol: ((obj.xtlsMethod == 0) ? 'SSLv23_method' : 'TLSv1_method'), ciphers: 'RSA+AES:!aNULL:!MD5:!DSS', secureOptions: obj.constants.SSL_OP_NO_SSLv2 | obj.constants.SSL_OP_NO_SSLv3 | obj.constants.SSL_OP_NO_COMPRESSION | obj.constants.SSL_OP_CIPHER_SERVER_PREFERENCE, rejectUnauthorized: false }; + var options = { secureProtocol: ((obj.xtlsMethod == 0) ? 'SSLv23_method' : 'TLSv1_method'), ciphers: 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:RSA+AES:!aNULL:!MD5:!DSS', secureOptions: obj.constants.SSL_OP_NO_SSLv2 | obj.constants.SSL_OP_NO_SSLv3 | obj.constants.SSL_OP_NO_COMPRESSION | obj.constants.SSL_OP_CIPHER_SERVER_PREFERENCE, rejectUnauthorized: false }; if (obj.xtlsoptions) { if (obj.xtlsoptions.ca) options.ca = obj.xtlsoptions.ca; if (obj.xtlsoptions.cert) options.cert = obj.xtlsoptions.cert; diff --git a/forge.js/aesCipherSuites.js b/forge.js/aesCipherSuites.js index 7087ca6..e9208d7 100644 --- a/forge.js/aesCipherSuites.js +++ b/forge.js/aesCipherSuites.js @@ -48,6 +48,155 @@ tls.CipherSuites['TLS_RSA_WITH_AES_256_CBC_SHA'] = { initConnectionState: initConnectionState }; +// cipher suites with SHA-256 MAC +tls.CipherSuites['TLS_RSA_WITH_AES_128_CBC_SHA256'] = { + id: [0x00,0x3c], + name: 'TLS_RSA_WITH_AES_128_CBC_SHA256', + initSecurityParameters: function(sp) { + sp.bulk_cipher_algorithm = tls.BulkCipherAlgorithm.aes; + sp.cipher_type = tls.CipherType.block; + sp.enc_key_length = 16; + sp.block_length = 16; + sp.fixed_iv_length = 16; + sp.record_iv_length = 16; + sp.mac_algorithm = tls.MACAlgorithm.hmac_sha256; + sp.mac_length = 32; + sp.mac_key_length = 32; + sp.prf_algorithm = tls.PRFAlgorithm.tls_prf_sha256; + }, + initConnectionState: initConnectionState_sha256 +}; + +tls.CipherSuites['TLS_RSA_WITH_AES_256_CBC_SHA256'] = { + id: [0x00,0x3d], + name: 'TLS_RSA_WITH_AES_256_CBC_SHA256', + initSecurityParameters: function(sp) { + sp.bulk_cipher_algorithm = tls.BulkCipherAlgorithm.aes; + sp.cipher_type = tls.CipherType.block; + sp.enc_key_length = 32; + sp.block_length = 16; + sp.fixed_iv_length = 16; + sp.record_iv_length = 16; + sp.mac_algorithm = tls.MACAlgorithm.hmac_sha256; + sp.mac_length = 32; + sp.mac_key_length = 32; + sp.prf_algorithm = tls.PRFAlgorithm.tls_prf_sha256; + }, + initConnectionState: initConnectionState_sha256 +}; + +tls.CipherSuites['TLS_RSA_WITH_AES_128_GCM_SHA256'] = { + id: [0x00,0x9c], + name: 'TLS_RSA_WITH_AES_128_GCM_SHA256', + initSecurityParameters: function(sp) { + sp.bulk_cipher_algorithm = tls.BulkCipherAlgorithm.aes; + sp.cipher_type = tls.CipherType.aead; + sp.enc_key_length = 16; + sp.fixed_iv_length = 4; + sp.record_iv_length = 8; + sp.mac_algorithm = tls.MACAlgorithm.aead; + sp.mac_length = 16; + sp.mac_key_length = 0; + sp.auth_tag_length = 16; + sp.prf_algorithm = tls.PRFAlgorithm.tls_prf_sha256; + }, + initConnectionState: initConnectionState_gcm +}; + +tls.CipherSuites['TLS_RSA_WITH_AES_256_GCM_SHA384'] = { + id: [0x00,0x9d], + name: 'TLS_RSA_WITH_AES_256_GCM_SHA384', + initSecurityParameters: function(sp) { + sp.bulk_cipher_algorithm = tls.BulkCipherAlgorithm.aes; + sp.cipher_type = tls.CipherType.aead; + sp.enc_key_length = 32; + sp.fixed_iv_length = 4; + sp.record_iv_length = 8; + sp.mac_algorithm = tls.MACAlgorithm.aead; + sp.mac_length = 16; + sp.mac_key_length = 0; + sp.auth_tag_length = 16; + sp.prf_algorithm = tls.PRFAlgorithm.tls_prf_sha384; + }, + initConnectionState: initConnectionState_gcm +}; + +tls.CipherSuites['TLS_DHE_RSA_WITH_AES_256_GCM_SHA384'] = { + id: [0x00,0x9f], + name: 'TLS_DHE_RSA_WITH_AES_256_GCM_SHA384', + initSecurityParameters: function(sp) { + sp.bulk_cipher_algorithm = tls.BulkCipherAlgorithm.aes; + sp.cipher_type = tls.CipherType.aead; + sp.enc_key_length = 32; + sp.fixed_iv_length = 4; + sp.record_iv_length = 8; + sp.mac_algorithm = tls.MACAlgorithm.aead; + sp.mac_length = 16; + sp.mac_key_length = 0; + sp.auth_tag_length = 16; + sp.key_exchange_algorithm = 'dhe_rsa'; + sp.prf_algorithm = tls.PRFAlgorithm.tls_prf_sha384; + }, + initConnectionState: initConnectionState_gcm +}; + +tls.CipherSuites['TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384'] = { + id: [0xc0,0x30], + name: 'TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384', + initSecurityParameters: function(sp) { + sp.bulk_cipher_algorithm = tls.BulkCipherAlgorithm.aes; + sp.cipher_type = tls.CipherType.aead; + sp.enc_key_length = 32; + sp.fixed_iv_length = 4; + sp.record_iv_length = 8; + sp.mac_algorithm = tls.MACAlgorithm.aead; + sp.mac_length = 16; + sp.mac_key_length = 0; + sp.auth_tag_length = 16; + sp.key_exchange_algorithm = 'ecdhe_rsa'; + sp.prf_algorithm = tls.PRFAlgorithm.tls_prf_sha384; + }, + initConnectionState: initConnectionState_gcm +}; + +tls.CipherSuites['TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256'] = { + id: [0xc0,0x2f], + name: 'TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256', + initSecurityParameters: function(sp) { + sp.bulk_cipher_algorithm = tls.BulkCipherAlgorithm.aes; + sp.cipher_type = tls.CipherType.aead; + sp.enc_key_length = 16; + sp.fixed_iv_length = 4; + sp.record_iv_length = 8; + sp.mac_algorithm = tls.MACAlgorithm.aead; + sp.mac_length = 16; + sp.mac_key_length = 0; + sp.auth_tag_length = 16; + sp.key_exchange_algorithm = 'ecdhe_rsa'; + sp.prf_algorithm = tls.PRFAlgorithm.tls_prf_sha256; + }, + initConnectionState: initConnectionState_gcm +}; + +tls.CipherSuites['TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384'] = { + id: [0xc0,0x2c], + name: 'TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384', + initSecurityParameters: function(sp) { + sp.bulk_cipher_algorithm = tls.BulkCipherAlgorithm.aes; + sp.cipher_type = tls.CipherType.aead; + sp.enc_key_length = 32; + sp.fixed_iv_length = 4; + sp.record_iv_length = 8; + sp.mac_algorithm = tls.MACAlgorithm.aead; + sp.mac_length = 16; + sp.mac_key_length = 0; + sp.auth_tag_length = 16; + sp.key_exchange_algorithm = 'ecdhe_ecdsa'; + sp.prf_algorithm = tls.PRFAlgorithm.tls_prf_sha384; + }, + initConnectionState: initConnectionState_gcm +}; + function initConnectionState(state, c, sp) { var client = (c.entity === forge.tls.ConnectionEnd.client); @@ -283,6 +432,321 @@ function compareMacs(key, mac1, mac2) { return mac1 === mac2; } +function initConnectionState_sha256(state, c, sp) { + var client = (c.entity === tls.ConnectionEnd.client); + + // cipher setup + state.read.cipherState = { + init: false, + cipher: forge.cipher.createDecipher('AES-CBC', client ? + sp.keys.server_write_key : sp.keys.client_write_key), + iv: client ? sp.keys.server_write_IV : sp.keys.client_write_IV + }; + state.write.cipherState = { + init: false, + cipher: forge.cipher.createCipher('AES-CBC', client ? + sp.keys.client_write_key : sp.keys.server_write_key), + iv: client ? sp.keys.client_write_IV : sp.keys.server_write_IV + }; + state.read.cipherFunction = decrypt_aes_cbc_sha256; + state.write.cipherFunction = encrypt_aes_cbc_sha256; + + // MAC setup + state.read.macLength = state.write.macLength = sp.mac_length; + state.read.macFunction = state.write.macFunction = tls.hmac_sha256; +} + +function initConnectionState_gcm(state, c, sp) { + var client = (c.entity === tls.ConnectionEnd.client); + + // cipher setup for AEAD (AES-GCM) + state.read.cipherState = { + init: false, + cipher: forge.cipher.createDecipher('AES-GCM', client ? + sp.keys.server_write_key : sp.keys.client_write_key), + iv: client ? sp.keys.server_write_IV : sp.keys.client_write_IV, + sequenceNumber: [0, 0] + }; + state.write.cipherState = { + init: false, + cipher: forge.cipher.createCipher('AES-GCM', client ? + sp.keys.client_write_key : sp.keys.server_write_key), + iv: client ? sp.keys.client_write_IV : sp.keys.server_write_IV, + sequenceNumber: [0, 0] + }; + state.read.cipherFunction = decrypt_aes_gcm; + state.write.cipherFunction = encrypt_aes_gcm; + + // AEAD doesn't use separate MAC + state.read.macLength = state.write.macLength = 0; + state.read.macFunction = state.write.macFunction = null; +} + +/** + * Updates a 64-bit sequence number represented as two 32-bit integers. + * + * @param seqNum the sequence number to update. + */ +function updateSequenceNumber(seqNum) { + if(seqNum[1] === 0xFFFFFFFF) { + seqNum[1] = 0; + ++seqNum[0]; + } else { + ++seqNum[1]; + } +} + +/** + * Encrypts the TLSCompressed record into a TLSCipherText record using AES + * in CBC mode with SHA-256 MAC. + * + * @param record the TLSCompressed record to encrypt. + * @param s the ConnectionState to use. + * + * @return true on success, false on failure. + */ +function encrypt_aes_cbc_sha256(record, s) { + var rval = false; + + // append MAC to fragment, update sequence number + var mac = s.macFunction(s.macKey, s.sequenceNumber, record); + record.fragment.putBytes(mac); + s.updateSequenceNumber(); + + // TLS 1.1+ use an explicit IV every time to protect against CBC attacks + var iv; + if(record.version.minor === tls.Versions.TLS_1_0.minor) { + // use the pre-generated IV when initializing for TLS 1.0, otherwise use + // the residue from the previous encryption + iv = s.cipherState.init ? null : s.cipherState.iv; + } else { + iv = forge.random.getBytesSync(16); + } + + s.cipherState.init = true; + + // start cipher + var cipher = s.cipherState.cipher; + cipher.start({iv: iv}); + + // TLS 1.1+ write IV into output + if(record.version.minor >= tls.Versions.TLS_1_1.minor) { + cipher.output.putBytes(iv); + } + + // do encryption (default padding is appropriate) + cipher.update(record.fragment); + if(cipher.finish(encrypt_aes_cbc_sha256_padding)) { + // set record fragment to encrypted output + record.fragment = cipher.output; + record.length = record.fragment.length(); + rval = true; + } + + return rval; +} + +/** + * Handles padding for aes_cbc_sha256 in encrypt mode. + * + * @param blockSize the block size. + * @param input the input buffer. + * @param decrypt true in decrypt mode, false in encrypt mode. + * + * @return true on success, false on failure. + */ +function encrypt_aes_cbc_sha256_padding(blockSize, input, decrypt) { + return encrypt_aes_cbc_sha1_padding(blockSize, input, decrypt); +} + +/** + * Decrypts a TLSCipherText record into a TLSCompressed record using + * AES in CBC mode with SHA-256 MAC. + * + * @param record the TLSCipherText record to decrypt. + * @param s the ConnectionState to use. + * + * @return true on success, false on failure. + */ +function decrypt_aes_cbc_sha256(record, s) { + var rval = false; + + var iv; + if(record.version.minor === tls.Versions.TLS_1_0.minor) { + // use pre-generated IV when initializing for TLS 1.0, otherwise use the + // residue from the previous decryption + iv = s.cipherState.init ? null : s.cipherState.iv; + } else { + // TLS 1.1+ use an explicit IV every time to protect against CBC attacks + // that is appended to the record fragment + iv = record.fragment.getBytes(16); + } + + s.cipherState.init = true; + + // start cipher + var cipher = s.cipherState.cipher; + cipher.start({iv: iv}); + + // do decryption + cipher.update(record.fragment); + rval = cipher.finish(decrypt_aes_cbc_sha256_padding); + + // even if decryption fails, keep going to minimize timing attacks + + // decrypted data: + // first (len - 32) bytes = application data + // last 32 bytes = MAC + var macLen = s.macLength; + + // create a random MAC to check against should the mac length check fail + // Note: do this regardless of the failure to keep timing consistent + var mac = forge.random.getBytesSync(macLen); + + // get fragment and mac + var len = cipher.output.length(); + if(len >= macLen) { + record.fragment = cipher.output.getBytes(len - macLen); + mac = cipher.output.getBytes(macLen); + } else { + // bad data, but get bytes anyway to try to keep timing consistent + record.fragment = cipher.output.getBytes(); + } + record.fragment = forge.util.createBuffer(record.fragment); + record.length = record.fragment.length(); + + // see if data integrity checks out, update sequence number + var mac2 = s.macFunction(s.macKey, s.sequenceNumber, record); + s.updateSequenceNumber(); + rval = (mac2 === mac) && rval; + + return rval; +} + +/** + * Handles padding for aes_cbc_sha256 in decrypt mode. + * + * @param blockSize the block size. + * @param output the output buffer. + * @param decrypt true in decrypt mode, false in encrypt mode. + * + * @return true on success, false on failure. + */ +function decrypt_aes_cbc_sha256_padding(blockSize, output, decrypt) { + return decrypt_aes_cbc_sha1_padding(blockSize, output, decrypt); +} + +/** + * Encrypts the TLSCompressed record into a TLSCipherText record using AES + * in GCM mode (AEAD). + * + * @param record the TLSCompressed record to encrypt. + * @param s the ConnectionState to use. + * + * @return true on success, false on failure. + */ +function encrypt_aes_gcm(record, s) { + var rval = false; + + // construct explicit nonce (8 bytes) + var explicitNonce = forge.util.createBuffer(); + explicitNonce.putInt32(s.cipherState.sequenceNumber[0]); + explicitNonce.putInt32(s.cipherState.sequenceNumber[1]); + + // construct IV: fixed_iv + explicit_nonce + var iv = forge.util.createBuffer(); + iv.putBytes(s.cipherState.iv); + iv.putBytes(explicitNonce.getBytes()); + + // create additional data for AEAD + var additionalData = forge.util.createBuffer(); + additionalData.putInt32(s.cipherState.sequenceNumber[0]); + additionalData.putInt32(s.cipherState.sequenceNumber[1]); + additionalData.putByte(record.type); + additionalData.putByte(record.version.major); + additionalData.putByte(record.version.minor); + additionalData.putInt16(record.fragment.length()); + + // start cipher with IV and additional data + var cipher = s.cipherState.cipher; + cipher.start({ + iv: iv.getBytes(), + additionalData: additionalData.getBytes() + }); + + // encrypt the fragment + cipher.update(record.fragment); + if(cipher.finish()) { + // prepend explicit nonce to ciphertext + auth tag + record.fragment = forge.util.createBuffer(); + record.fragment.putBytes(explicitNonce.getBytes()); + record.fragment.putBytes(cipher.output.getBytes()); + record.fragment.putBytes(cipher.mode.tag.getBytes()); + record.length = record.fragment.length(); + rval = true; + + // update sequence number + updateSequenceNumber(s.cipherState.sequenceNumber); + } + + return rval; +} + +/** + * Decrypts a TLSCipherText record into a TLSCompressed record using + * AES in GCM mode (AEAD). + * + * @param record the TLSCipherText record to decrypt. + * @param s the ConnectionState to use. + * + * @return true on success, false on failure. + */ +function decrypt_aes_gcm(record, s) { + var rval = false; + + // extract explicit nonce (8 bytes) + var explicitNonce = record.fragment.getBytes(8); + + // extract auth tag (16 bytes from end) + var ciphertext = record.fragment.getBytes(record.fragment.length() - 16); + var authTag = record.fragment.getBytes(16); + + // construct IV: fixed_iv + explicit_nonce + var iv = forge.util.createBuffer(); + iv.putBytes(s.cipherState.iv); + iv.putBytes(explicitNonce); + + // create additional data for AEAD + var additionalData = forge.util.createBuffer(); + additionalData.putInt32(s.cipherState.sequenceNumber[0]); + additionalData.putInt32(s.cipherState.sequenceNumber[1]); + additionalData.putByte(record.type); + additionalData.putByte(record.version.major); + additionalData.putByte(record.version.minor); + additionalData.putInt16(ciphertext.length); + + // start cipher with IV, additional data, and auth tag + var cipher = s.cipherState.cipher; + cipher.start({ + iv: iv.getBytes(), + additionalData: additionalData.getBytes(), + tag: authTag + }); + + // decrypt the ciphertext + cipher.update(forge.util.createBuffer(ciphertext)); + if(cipher.finish()) { + record.fragment = cipher.output; + record.length = record.fragment.length(); + rval = true; + + // update sequence number + updateSequenceNumber(s.cipherState.sequenceNumber); + } + + return rval; +} + } // end module implementation /* ########## Begin module wrapper ########## */ diff --git a/forge.js/dhe.js b/forge.js/dhe.js new file mode 100644 index 0000000..258ecb6 --- /dev/null +++ b/forge.js/dhe.js @@ -0,0 +1,118 @@ +/** + * DHE (Diffie-Hellman Ephemeral) implementation for Forge TLS. + */ +(function() { +/* ########## Begin module implementation ########## */ +function initModule(forge) { + +var tls = forge.tls; + +// Diffie-Hellman implementation for DHE support +tls.dh = { + // 2048-bit MODP Group 14 (RFC 3526) - well-known safe prime + p2048: new forge.jsbn.BigInteger( + 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1' + + '29024E088A67CC74020BBEA63B139B22514A08798E3404DD' + + 'EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245' + + 'E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' + + 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D' + + 'C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F' + + '83655D23DCA3AD961C62F356208552BB9ED529077096966D' + + '670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B' + + 'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9' + + 'DE2BCBF6955817183995497CEA956AE515D2261898FA0510' + + '15728E5A8AACAA68FFFFFFFFFFFFFFFF', 16), + + g2048: new forge.jsbn.BigInteger('2', 16), + + generateKeyPair: function(p, g) { + p = p || tls.dh.p2048; + g = g || tls.dh.g2048; + + // Generate random private key (1 < x < p-1) + var pMinus1 = p.subtract(forge.jsbn.BigInteger.ONE); + var privateKey; + + do { + // Generate random bytes for private key + var bytes = forge.random.getBytesSync(32); // 256 bits + privateKey = new forge.jsbn.BigInteger(forge.util.bytesToHex(bytes), 16); + } while (privateKey.compareTo(forge.jsbn.BigInteger.ONE) <= 0 || + privateKey.compareTo(pMinus1) >= 0); + + // Calculate public key: g^x mod p + var publicKey = g.modPow(privateKey, p); + + return { + privateKey: privateKey, + publicKey: publicKey + }; + }, + + computeSecret: function(privateKey, theirPublicKey, p) { + p = p || tls.dh.p2048; + + // Validate that their public key is in valid range (1 < Y < p-1) + if(theirPublicKey.compareTo(forge.jsbn.BigInteger.ONE) <= 0 || + theirPublicKey.compareTo(p) >= 0) { + throw new Error('Invalid DH public key'); + } + + // Compute shared secret: Y^x mod p + return theirPublicKey.modPow(privateKey, p); + } +}; + +} // end module implementation + +/* ########## Begin module wrapper ########## */ +var name = 'dhe'; +if(typeof define !== 'function') { + // NodeJS -> AMD + if(typeof module === 'object' && module.exports) { + var nodeJS = true; + define = function(ids, factory) { + factory(require, module); + }; + } else { + //