Pages

Monday, December 9, 2013

node.js, DH key exchanges & HMAC

What the !@#$ are you talking about?

Security is difficult to understand, cryptology doubly so. I recently read a wonderful blog post regarding 'cryptographic right answers'.

Seeing as this particular blog came from an expert in the field of cryptology implementation (et al; the developer of 'scrypt' hashing algorithm) and security (former BSD security officer), I think it is worth a read.

If your in a rush, the summary goes somewhere along the lines of 'use xyz algorithm due to the following abc strengths' and 'always use an HMAC to prevent tampering'.

Use SSL/TLS! blah blah!

Yes, I agree, however this protocol is not without its faults and weaknesses

The majority of my interest in this stems from researching methods to prevent or isolate and protect HTTP header & content in the event of a cipher downgrade, side channel, brute force, session renegotiation attack stemming from a traditional MITM or BGP routing bug presents itself.

Use oAuth! blah, blah!

This is also a very valid point and I also agree. However, leaving these fields visibly defined makes it easy in the event of a 'replay attack'.

The majority of today's HMAC use within the HTTP/HTTPS protocols get embedded within the browser response or the clients request headers as shown in figure 1 and defined in the IETF oAuth draft. The 'oauth_signature' field is clearly shown. (To test or see more examples of the oAuth header values, please visit hueniverse.com)

Figure 1
GET /?abc=123 HTTP/1.1
Host: abc.com:443
Authorization: OAuth realm="https://abc.com/",
    oauth_consumer_key="xyz",
    oauth_token="456",
    oauth_nonce="213",
    oauth_timestamp="0",
    oauth_signature_method="HMAC-SHA1",
    oauth_version="2.0",
    oauth_signature="Q%2BuWrnEFBv8CEwtNuxn3bIdkjsY%3D"

In the event of a communications channel being compromised the 'oauth_signature' field can be easily recalculated by an attacker based on the modified contents leaving the server/client none-the-wiser that their message has indeed been modified.

While the concept of utilizing a HMAC to verify a message is not a new concept, the question arises 'How can one use an HMAC effectively in an scenario where the TLS/SSL communications channel has been compromised?'

Ok, ok you made you point, now what?!

While I realize that my proposal is not a standard or even typical solution to the above mentioned attack vectors, it should prove beneficial if and when the need presents itself.

Figure 2 is an exportable module to assist in creating a 'hidden' payload which contains a valid HMAC digest & data payload. This of course could be expanded to accommodate for the use of a 'nonce' as well as a 'timestamp' (both can be very beneficial, and recommended, when it comes to providing the utmost security from attacks).

Figure 1
/*
 * node-dhcp-manager
 *
 * Copyright 2013 Jason Gerfen
 * All rights reserved.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

exports.createPayload = function(payload, digest, count) {
  var a = [],
      b = digest.split(''),
      iteration = count || 3,
      ret = []

  do {
    a.push(payload.substring(0, iteration))
  } while((payload = payload.substring(iteration, payload.length)) != "")

  for (var n = 0; n < a.length; n++) {
    if (b[n] && a[n]) {
        ret.push(a[n]+b[n]);
    } else if(b[n] && !a[n]) {
        ret.push(b[n]);
    } else if(!b[n] && a[n]) {
        ret.push(a[n])
    }
  }
  return ret.join('')
}

exports.extractPayload = function(payload, size, count) {
  var num = count + 1 || 4,
      regex1 = new RegExp('.{1,'+num+'}', 'g'),
      data1 = payload.match(regex1),
      regex2 = new RegExp('.{1,'+num+'}', 'g'),
      data2 = payload.match(regex2),
      digest = [],
      data = [],
      i = 0

  for (var n = 0; n < size; n++) {
    digest.push(data1[n].substr(count, data1[n].length))
  }

  data2.forEach(function(item) {
    if (i < size) {
      data.push(item.substr(0, item.length - 1))
    } else {
      data.push(item)
    }
    i++
  })

  return {
    digest: digest.join(''),
    payload: data.join('')
  }
}

What does this have to do with a DH exchange?!

Patience is a virtue! To utilize this particular module please refer to figure 3 which 'simulates' a Diffie-Hellman key exchange between two parties.

Figure 3
/*
 * node-dhcp-manager
 *
 * Copyright 2013 Jason Gerfen
 * All rights reserved.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

var chai = require('chai'),
    expect = chai.expect,
    should = chai.should(),
    crypto = require('../libs/crypto'),
    hs = require('../libs/handshake'),
    bob = crypto.init(),
    alice = crypto.init(),
    bobSecret = crypto.createSharedSecret(alice.pubKey),
    bobPubKey = crypto.encryptData(bobSecret, bob.pubKey),
    bobDigest = crypto.createDigest(bobSecret, bobPubKey),
    bobPayload = hs.createPayload(bobPubKey, bobDigest, 3)
    bobObj = hs.extractPayload(bobPayload, 128, 3),
    aliceSecret = crypto.createSharedSecret(bob.pubKey),
    alicePubKey = crypto.encryptData(aliceSecret, alice.pubKey),
    aliceDigest = crypto.createDigest(aliceSecret, alicePubKey)
    alicePayload = hs.createPayload(alicePubKey, aliceDigest, 3),
    aliceObj = hs.extractPayload(alicePayload, 128, 3)

describe('handshake.js', function(){

  describe('DH exchange', function(){
    it('bob & alice must share secret', function(done){

      expect(bobSecret).to.equal(aliceSecret)

      done()
    })
  })

  describe('create payload', function(){
    it('must return valid payload', function(done){

      expect(bobPayload).to.have.length(4224)
      expect(alicePayload).to.have.length(4224)

      done()
    })
  })

  describe('extract data from payload', function(){
    it('must contain valid payload & digest', function(done){

      bobObj.should.be.a('object')
      expect(bobObj).to.have.property('digest').with.length(128)
      expect(bobObj).to.have.property('payload').with.length(4096)

      aliceObj.should.be.a('object')
      expect(aliceObj).to.have.property('digest').with.length(128)
      expect(aliceObj).to.have.property('payload').with.length(4096)

      done()
    })
  })

  describe('verify digest', function(){
    it('must contain valid digest of payload', function(done){

      expect(bobObj.digest).to.equal(bobDigest)
      expect(aliceObj.digest).to.equal(aliceDigest)

      done()
    })
  })

  describe('verify keys', function(){
    it('must contain matching keys from originals', function(done){

      expect(crypto.decryptData(bobSecret, bobObj.payload)).to.equal(bob.pubKey)
      expect(crypto.decryptData(aliceSecret, aliceObj.payload)).to.equal(alice.pubKey)

      done()
    })
  })
})

Shameless plug

The above code snippets come from a much larger project which can be found @ node-dhcp-manager.

No comments:

Post a Comment