Skip to content

Commit

Permalink
When authenticating, also test epoch boundaries.
Browse files Browse the repository at this point in the history
On occasion, a client's or a server's epoch might already have increased
whereas the epoch of the other party didn't.  This is a benign event and there
is no reason to fail authentication because of this.  As a result, as a server,
we now also test boundary values, i.e., epoch - 1, epoch, epoch + 1.
  • Loading branch information
NullHypothesis committed Mar 8, 2014
1 parent d4d8ebc commit c549683
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 16 deletions.
22 changes: 14 additions & 8 deletions scramblesuit.py
Expand Up @@ -388,14 +388,20 @@ def receiveTicket( self, data ):
existingHMAC = potentialTicket[index + const.MARK_LENGTH:
index + const.MARK_LENGTH +
const.HMAC_SHA256_128_LENGTH]
myHMAC = mycrypto.HMAC_SHA256_128(self.recvHMAC,
potentialTicket[0:
index + const.MARK_LENGTH] +
util.getEpoch())

if not util.isValidHMAC(myHMAC, existingHMAC, self.recvHMAC):
log.warning("The HMAC is invalid: `%s' vs. `%s'." %
(myHMAC.encode('hex'), existingHMAC.encode('hex')))
authenticated = False
for epoch in util.expandedEpoch():
myHMAC = mycrypto.HMAC_SHA256_128(self.recvHMAC,
potentialTicket[0:index + \
const.MARK_LENGTH] + epoch)

if util.isValidHMAC(myHMAC, existingHMAC, self.recvHMAC):
authenticated = True
break

log.debug("HMAC invalid. Trying next epoch value.")

if not authenticated:
log.warning("Could not verify the authentication message's HMAC.")
return False

# Do nothing if the ticket is replayed. Immediately closing the
Expand Down
22 changes: 14 additions & 8 deletions uniformdh.py
Expand Up @@ -120,19 +120,25 @@ def extractPublicKey( self, data, srvState=None ):
if not index:
return False

self.echoEpoch = util.getEpoch()

# Now that we know where the authenticating HMAC is: verify it.
hmacStart = index + const.MARK_LENGTH
existingHMAC = handshake[hmacStart:
(hmacStart + const.HMAC_SHA256_128_LENGTH)]
myHMAC = mycrypto.HMAC_SHA256_128(self.sharedSecret,
handshake[0 : hmacStart] +
self.echoEpoch)

if not util.isValidHMAC(myHMAC, existingHMAC, self.sharedSecret):
log.warning("The HMAC is invalid: `%s' vs. `%s'." %
(myHMAC.encode('hex'), existingHMAC.encode('hex')))
authenticated = False
for epoch in util.expandedEpoch():
myHMAC = mycrypto.HMAC_SHA256_128(self.sharedSecret,
handshake[0 : hmacStart] + epoch)

if util.isValidHMAC(myHMAC, existingHMAC, self.sharedSecret):
self.echoEpoch = epoch
authenticated = True
break

log.debug("HMAC invalid. Trying next epoch value.")

if not authenticated:
log.warning("Could not verify the authentication message's HMAC.")
return False

# Do nothing if the ticket is replayed. Immediately closing the
Expand Down
57 changes: 57 additions & 0 deletions unittests.py
Expand Up @@ -9,6 +9,8 @@
import base64
import shutil
import tempfile
import ticket
import state

import message

Expand Down Expand Up @@ -160,6 +162,29 @@ def test3_invalidHMAC( self ):

self.failIf(self.udh.receivePublicKey(buf, lambda x: x) == True)

def test4_extractPublicKey( self ):

# Create UniformDH authentication message.
sharedSecret = "A" * const.SHARED_SECRET_LENGTH

realEpoch = util.getEpoch

# Try three valid and one invalid epoch value.
for epoch in util.expandedEpoch() + ["000000"]:
udh = uniformdh.new(sharedSecret, True)

util.getEpoch = lambda: epoch
authMsg = udh.createHandshake()
util.getEpoch = realEpoch

buf = obfs_buf.Buffer()
buf.write(authMsg)

if epoch == "000000":
self.assertFalse(udh.extractPublicKey(buf))
else:
self.assertTrue(udh.extractPublicKey(buf))

class UtilTest( unittest.TestCase ):

def test1_isValidHMAC( self ):
Expand Down Expand Up @@ -308,6 +333,38 @@ def test4_ProtocolMessage( self ):
self.assertRaises(base.PluggableTransportError,
message.ProtocolMessage, "1", paddingLen=const.MPU)

class TicketTest( unittest.TestCase ):

def test1_authentication( self ):
srvState = state.State()
srvState.genState()

ss = scramblesuit.ScrambleSuitTransport()
ss.srvState = srvState

realEpoch = util.getEpoch

# Try three valid and one invalid epoch value.
for epoch in util.expandedEpoch() + ["000000"]:

util.getEpoch = lambda: epoch

# Prepare ticket message.
blurb = ticket.issueTicketAndKey(srvState)
rawTicket = blurb[const.MASTER_KEY_LENGTH:]
masterKey = blurb[:const.MASTER_KEY_LENGTH]
ss.deriveSecrets(masterKey)
ticketMsg = ticket.createTicketMessage(rawTicket, ss.recvHMAC)

util.getEpoch = realEpoch

buf = obfs_buf.Buffer()
buf.write(ticketMsg)

if epoch == "000000":
self.assertFalse(ss.receiveTicket(buf))
else:
self.assertTrue(ss.receiveTicket(buf))

if __name__ == '__main__':
# Disable all logging as it would yield plenty of warning and error
Expand Down
10 changes: 10 additions & 0 deletions util.py
Expand Up @@ -106,6 +106,16 @@ def getEpoch( ):
return str(int(time.time()) / const.EPOCH_GRANULARITY)


def expandedEpoch( ):
"""
Return [epoch, epoch-1, epoch+1].
"""

epoch = int(getEpoch())

return [str(epoch), str(epoch - 1), str(epoch + 1)]


def writeToFile( data, fileName ):
"""
Writes the given `data' to the file specified by `fileName'.
Expand Down

0 comments on commit c549683

Please sign in to comment.