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))
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))
def receiveTicket(self, data): """ Extract and verify a potential session ticket. The given `data' is treated as a session ticket. The ticket is being decrypted and authenticated (yes, in that order). If all these steps succeed, `True' is returned. Otherwise, `False' is returned. """ if len(data) < (const.TICKET_LENGTH + const.MARK_LENGTH + const.HMAC_SHA256_128_LENGTH): return False potentialTicket = data.peek() # Now try to decrypt and parse the ticket. We need the master key # inside to verify the HMAC in the next step. if not self.decryptedTicket: newTicket = ticket.decrypt(potentialTicket[:const.TICKET_LENGTH], self.srvState) if newTicket != None and newTicket.isValid(): self.deriveSecrets(newTicket.masterKey) self.decryptedTicket = True else: return False # First, find the mark to efficiently locate the HMAC. mark = mycrypto.HMAC_SHA256_128(self.recvHMAC, potentialTicket[:const.TICKET_LENGTH]) index = util.locateMark(mark, potentialTicket) if not index: return False # Now, verify if the HMAC is valid. existingHMAC = potentialTicket[index + const.MARK_LENGTH:index + const.MARK_LENGTH + const.HMAC_SHA256_128_LENGTH] 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 # connection would be suspicious. if self.srvState.isReplayed(existingHMAC): log.warning("The HMAC was already present in the replay table.") return False data.drain(index + const.MARK_LENGTH + const.HMAC_SHA256_128_LENGTH) log.debug("Adding the HMAC authenticating the ticket message to the " \ "replay table: %s." % existingHMAC.encode('hex')) self.srvState.registerKey(existingHMAC) log.debug("Switching to state ST_CONNECTED.") self.protoState = const.ST_CONNECTED return True
def extractPublicKey(self, data, srvState=None): """ Extract and return a UniformDH public key out of `data'. Before the public key is touched, the HMAC is verified. If the HMAC is invalid or some other error occurs, `False' is returned. Otherwise, the public key is returned. The extracted data is finally drained from the given `data' object. """ assert self.sharedSecret is not None # Do we already have the minimum amount of data? if len(data) < (const.PUBLIC_KEY_LENGTH + const.MARK_LENGTH + const.HMAC_SHA256_128_LENGTH): return False log.debug("Attempting to extract the remote machine's UniformDH " "public key out of %d bytes of data." % len(data)) handshake = data.peek() # First, find the mark to efficiently locate the HMAC. publicKey = handshake[:const.PUBLIC_KEY_LENGTH] mark = mycrypto.HMAC_SHA256_128(self.sharedSecret, publicKey) index = util.locateMark(mark, handshake) if not index: return False # 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)] 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 # connection would be suspicious. if srvState is not None and srvState.isReplayed(existingHMAC): log.warning("The HMAC was already present in the replay table.") return False data.drain(index + const.MARK_LENGTH + const.HMAC_SHA256_128_LENGTH) if srvState is not None: log.debug("Adding the HMAC authenticating the UniformDH message " \ "to the replay table: %s." % existingHMAC.encode('hex')) srvState.registerKey(existingHMAC) return handshake[:const.PUBLIC_KEY_LENGTH]
def receiveTicket( self, data ): """ Extract and verify a potential session ticket. The given `data' is treated as a session ticket. The ticket is being decrypted and authenticated (yes, in that order). If all these steps succeed, `True' is returned. Otherwise, `False' is returned. """ if len(data) < (const.TICKET_LENGTH + const.MARK_LENGTH + const.HMAC_SHA256_128_LENGTH): return False potentialTicket = data.peek() # Now try to decrypt and parse the ticket. We need the master key # inside to verify the HMAC in the next step. if not self.decryptedTicket: newTicket = ticket.decrypt(potentialTicket[:const.TICKET_LENGTH], self.srvState) if newTicket != None and newTicket.isValid(): self.deriveSecrets(newTicket.masterKey) self.decryptedTicket = True else: return False # First, find the mark to efficiently locate the HMAC. mark = mycrypto.HMAC_SHA256_128(self.recvHMAC, potentialTicket[:const.TICKET_LENGTH]) index = util.locateMark(mark, potentialTicket) if not index: return False # Now, verify if the HMAC is valid. existingHMAC = potentialTicket[index + const.MARK_LENGTH: index + const.MARK_LENGTH + const.HMAC_SHA256_128_LENGTH] 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 # connection would be suspicious. if self.srvState.isReplayed(existingHMAC): log.warning("The HMAC was already present in the replay table.") return False data.drain(index + const.MARK_LENGTH + const.HMAC_SHA256_128_LENGTH) log.debug("Adding the HMAC authenticating the ticket message to the " \ "replay table: %s." % existingHMAC.encode('hex')) self.srvState.registerKey(existingHMAC) log.debug("Switching to state ST_CONNECTED.") self.protoState = const.ST_CONNECTED return True
def extractPublicKey( self, data, srvState=None ): """ Extract and return a UniformDH public key out of `data'. Before the public key is touched, the HMAC is verified. If the HMAC is invalid or some other error occurs, `False' is returned. Otherwise, the public key is returned. The extracted data is finally drained from the given `data' object. """ assert self.sharedSecret is not None # Do we already have the minimum amount of data? if len(data) < (const.PUBLIC_KEY_LENGTH + const.MARK_LENGTH + const.HMAC_SHA256_128_LENGTH): return False log.debug("Attempting to extract the remote machine's UniformDH " "public key out of %d bytes of data." % len(data)) handshake = data.peek() # First, find the mark to efficiently locate the HMAC. publicKey = handshake[:const.PUBLIC_KEY_LENGTH] mark = mycrypto.HMAC_SHA256_128(self.sharedSecret, publicKey) index = util.locateMark(mark, handshake) if not index: return False # 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)] 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 # connection would be suspicious. if srvState is not None and srvState.isReplayed(existingHMAC): log.warning("The HMAC was already present in the replay table.") return False data.drain(index + const.MARK_LENGTH + const.HMAC_SHA256_128_LENGTH) if srvState is not None: log.debug("Adding the HMAC authenticating the UniformDH message " \ "to the replay table: %s." % existingHMAC.encode('hex')) srvState.registerKey(existingHMAC) return handshake[:const.PUBLIC_KEY_LENGTH]