class VerifyCrawler(threading.Thread): ''' This is an active thread that is responsible for serving all messages that come in from the keylogger. It simply strips them out of the socket and forwards them along to the logger actor via a message dictionary. ''' def __init__(self, vid, logServer, keyServer, keyMgr): ''' Constructor that stores the log server information. ''' threading.Thread.__init__(self) self.id = vid self.logServer = logServer self.keyServer = keyServer self.running = True # Build the encryption module self.keyMgr = keyMgr self.encryptionModule = EncryptionModule(keyMgr) # pass along the key manager reference # Generate the used entry bucket self.usedBin = {} self.MAX_TRIES = 10 # This can (and should) be configured by experimentation. # Configure the logger logFile = 'abls.log' logging.basicConfig(filename=logFile,level=logging.DEBUG) def run(self): ''' The main thread loop for this verifier. ''' # Create the shim self.logShim = DBShim(self.logServer, self.keyMgr) self.keyShim = DBShim(self.keyServer, self.keyMgr) # Run the crawler loop indefinitely... while self.running: logging.debug("Verifier " + str(self.id) + " is trying to grab a user session to verify.") (userId, sessionId) = self.selectRow() # Check to see if we found some valid data... if (userId != -1 and sessionId != -1): # Decrypt the user/session ID to get the original data userCT = userId.decode("hex") sessionCT = sessionId.decode("hex") key = hashlib.sha256(self.keyMgr.getMasterKey() + "log").digest() cipher = AES.new(key, AES.MODE_ECB) userPT = cipher.decrypt(userCT) sessionPT = cipher.decrypt(sessionCT) # Query the keys from the database logging.debug("Verifying: " + str(userPT) + " - " + str(sessionPT)) valueMap = {"userId" : userPT, "sessionId" : sessionPT} epochKey = self.keyShim.executeMultiQuery("initialEpochKey", valueMap, ["userId", "sessionId"]) key1 = epochKey[0]["key"] entityKey = self.keyShim.executeMultiQuery("initialEntityKey", valueMap, ["userId", "sessionId"]) key2 = entityKey[0]["key"] # Decrypt the keys using the 'verifier' policy logging.debug("Trying to decrypt") sk = self.encryptionModule.generateUserKey(['VERIFIER']) k1 = self.encryptionModule.decrypt(sk, key1)[1] # [1] to pull out plaintext, [0] is T/F flag k2 = self.encryptionModule.decrypt(sk, key2)[1] # [1] to pull out plaintext, [0] is T/F flag # Query the last digest from the database logging.debug("Decryption successful - continue with the verification process") entityDigest = self.logShim.executeMultiQuery("entity", valueMap, ["userId", "sessionId"]) digest = entityDigest[len(entityDigest) - 1]["digest"] # Query for the log now. valueMap = {"userId" : userId, "sessionId" : sessionId} logResult = self.logShim.executeMultiQuery("log", valueMap, []) log = {} userId = int(userPT) sessionId = int(sessionPT) log[(userId, sessionId)] = [] for i in range(0, len(logResult)): log[(userId, sessionId)].append([userId, sessionId, logResult[i]["epochId"], logResult[i]["message"], logResult[i]["xhash"], logResult[i]["yhash"]]) # Verify the data extracted from the database... self.strongestVerify(userId, sessionId, log, k1, k2, digest, Logger.Logger.EPOCH_WINDOW_SIZE) # Don't hog the system resources time.sleep(15) def selectRow(self): ''' Randomly select a row from the database to check with strong verification. ''' userId = sessionId = 0 foundNewRow = False tries = 0 while not foundNewRow: result = self.logShim.randomQuery("log") if (len(result) > 0): userId = result[0]["userId"] sessionId = result[0]["sessionId"] if not ((userId, sessionId) in self.usedBin): self.usedBin[(userId, sessionId)] = 0 foundNewRow = True # Upgrade all the instances for for key in self.usedBin.keys(): self.usedBin[key] = self.usedBin[key] + 1 # See if we ran past the try cap tries = tries + 1 if (tries >= self.MAX_TRIES): tk1, tk2, maxNum = 0, 0, 0 for (k1, k2) in self.usedBin.keys(): if (self.usedBin[(k1, k2)] > maxNum): maxNum = self.usedBin[(k1, k2)] tk1 = -1 tk2 = -1 del self.usedBin[(tk1, tk2)] userId = tk1 sessionId = tk2 foundNewRow = True # we're going to retry a previous row else: userId = sessionId = -1 foundNewRow = True return (userId, sessionId) def strongestVerify(self, userId, sessionId, log, epochKey, entityKey, lastDigest, EPOCH_WINDOW_SIZE = Logger.Logger.EPOCH_WINDOW_SIZE): ''' Walks the log chain and epoch chain for verification, and computes the entity digests at every epoch cycle for comparison to check with the end result. Not publicly verifiable, and requires the initial epoch and entity keys. ''' ctChain = [] sha3 = Keccak.Keccak() # It is assumed that we would get this initial key from the trusted server... # This verification scheme is not possible without the epoch key... lastEpochDigest = hmac.new(epochKey, "0", hashlib.sha512).hexdigest() # Check to see if we even have anything to verify if not ((userId, sessionId) in log): return None else: # Handle the base of the chain first = log[(userId, sessionId)][0] firstPayload = str(userId) + str(sessionId) + str(0) + str(first[3]) + str(0) # Check the hash chain first xi = sha3.Keccak((len(bytes(firstPayload)), firstPayload.encode("hex"))) computedV = sha3.Keccak((len(xi), xi)) assert(xi == first[4]) # Check the epoch chain next yi = hmac.new(epochKey, lastEpochDigest.encode("hex") + first[4].encode("hex"), hashlib.sha512).hexdigest() assert(yi == first[5]) # Compute the first part of the entity chain now lastEntityDigest = hmac.new(entityKey, xi, hashlib.sha512).hexdigest() entityKey = hmac.new(entityKey, "some constant value", hashlib.sha512).hexdigest() # Append the first message. ctChain.append(first[3]) # Walk the chain and make sure we can verify it... for i in range(1, len(log[(userId, sessionId)])): first = log[(userId, sessionId)][i] # Store the message firstMessage = first[3] # the message ctChain.append(firstMessage) # The other data... currentHash = first[4] # the hash previousHash = log[(userId, sessionId)][i - 1][4] # Verify that the first entry is correct firstPayload = str(userId) + str(0) + str(i) + str(firstMessage) + str(previousHash) firstComputedHash = sha3.Keccak((len(bytes(firstPayload)), firstPayload.encode("hex"))) assert(currentHash == firstComputedHash) # Check the epoch chain to see if we need to cycle if ((i % EPOCH_WINDOW_SIZE) == 0): # Update the epoch key currKey = epochKey newKey = sha3.Keccak((len(bytes(currKey)), currKey.encode("hex"))) epochKey = newKey # Pull the last hash block length = len(log[(userId, sessionId)]) lastHash = log[(userId, sessionId)][i - 1][4] # Form the epoch block hash payload payload = str(lastEpochDigest) + str(lastHash) lastEpochDigest = hmac.new(newKey, payload, hashlib.sha512).hexdigest() # Compute the epoch chain value yi = hmac.new(epochKey, lastEpochDigest.encode("hex") + first[4].encode("hex"), hashlib.sha512).hexdigest() assert(yi == first[5]) # Compute the first part of the entity chain now lastEntityDigest = hmac.new(entityKey, first[4], hashlib.sha512).hexdigest() entityKey = hmac.new(entityKey, "some constant value", hashlib.sha512).hexdigest() assert(lastEntityDigest == lastDigest) logging.debug("Verification result:" + str(lastEntityDigest == lastDigest)) return ctChain def weakVerify(self, userId, sessionId, log, epochKey, entityKey, EPOCH_WINDOW_SIZE): ''' Only walks the log chain for verification. ''' ctChain = [] # Make sure we have something to verify first... if not ((userId, sessionId) in log): return None else: # Handle the base of the chain first = log[(userId, sessionId)][0] firstPayload = str(userId) + str(sessionId) + str(0) + str(first[3]) + str(0) digest = sha3.Keccak((len(bytes(firstPayload)), firstPayload.encode("hex"))) assert(digest == first[4]) # Append the first message. ctChain.append(first[3]) # Walk the chain and make sure we can verify it... for i in range(1, len(log[(userId, sessionId)])): first = log[(userId, sessionId)][i] # Store the message firstMessage = first[3] # the message ctChain.append(firstMessage) # The other data... currentHash = first[4] # the hash previousHash = log[(userId, sessionId)][i - 1][4] # Verify that the first entry is correct firstPayload = str(userId) + str(0) + str(i) + str(firstMessage) + str(previousHash) firstComputedHash = sha3.Keccak((len(bytes(firstPayload)), firstPayload.encode("hex"))) assert(currentHash == firstComputedHash) return ctChain
# Test before sharing (mk1, pk1) = enc1.getValues() (mk2, pk2) = enc2.getValues() print("Master keys (before sharing)") print(objectToBytes(mk1, PairingGroup('SS512')) == objectToBytes(mk2, PairingGroup('SS512'))) print("Public keys (before sharing)") print(objectToBytes(pk1, PairingGroup('SS512')) == objectToBytes(pk2, PairingGroup('SS512'))) # Test before sharing the keys ct1 = enc1.encrypt(msg, policy) ct2 = enc2.encrypt(msg, policy) sk1 = enc1.generateUserKey(attrs) # takes a list of attributes (in caps?) sk2 = enc2.generateUserKey(attrs) # takes a list of attributes (in caps?) #print(enc1.decrypt(sk1, ct2)[1]) print(enc1.decrypt(sk2, ct2)[1]) # Dipslay master keys (mk1, pk1) = enc1.getValues() (mk2, pk2) = enc2.getValues() enc2.set(mk1, pk1) (mk2, pk2) = enc2.getValues() print("Master keys (after sharing)") print(objectToBytes(mk1, PairingGroup('SS512')) == objectToBytes(mk2, PairingGroup('SS512'))) print("Public keys (after sharing)") print(objectToBytes(pk1, PairingGroup('SS512')) == objectToBytes(pk2, PairingGroup('SS512'))) ct1 = enc1.encrypt(msg, policy) ct2 = enc2.encrypt(msg, policy) sk1 = enc1.generateUserKey(attrs) # takes a list of attributes (in caps?) sk2 = enc2.generateUserKey(attrs) # takes a list of attributes (in caps?)