# # Copyright 2008-2018 Clipperz Srl # # This file is part of Clipperz, the online password manager. # For further information about its features and functionalities please # refer to http://www.clipperz.com. # # * Clipperz is free software: you can redistribute it and/or modify it # under the terms of the GNU Affero General Public License as published # by the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # * Clipperz is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the GNU Affero General Public License for more details. # # * You should have received a copy of the GNU Affero General Public # License along with Clipperz. If not, see http://www.gnu.org/licenses/. # import os import cgi import wsgiref.handlers import datetime import uuid import random import hashlib import logging from google.appengine.api import users from google.appengine.ext import webapp from google.appengine.ext import db from google.appengine.ext.webapp import template from django.utils import simplejson #============================================================================== sessionTimeout = datetime.timedelta(minutes=-2) def randomSeed(): return hex(random.getrandbits(32*8))[2:-1] def clipperzHash(aString): #logging.info(">>> string: " + aString) firstRound = hashlib.sha256() firstRound.update(aString) #logging.info("firstRound: " + firstRound.hexdigest() + " - " + firstRound.digest()) result = hashlib.sha256() result.update(firstRound.digest()) #logging.info("<<< finalResul: " + result.hexdigest()) return result.hexdigest() #============================================================================== class User(db.Model): username = db.StringProperty() srp_s = db.StringProperty() srp_v = db.StringProperty() header = db.TextProperty() statistics = db.TextProperty() auth_version= db.StringProperty() version = db.StringProperty() lock = db.StringProperty() def updateCredentials(self, someCredentials): self.username = someCredentials['C'] self.srp_s = someCredentials['s'] self.srp_v = someCredentials['v'] self.auth_version = someCredentials['version'] def update(self, someData): self.header = someData['header'] self.statistics = someData['statistics'] self.version = someData['version'] self.lock = someData['lock'] #------------------------------------------------------------------------------ class Record(db.Model): user = db.ReferenceProperty(User) reference = db.StringProperty() data = db.TextProperty() version = db.StringProperty() creation_date = db.DateTimeProperty(auto_now_add=True) update_date = db.DateTimeProperty(auto_now_add=True) access_date = db.DateTimeProperty(auto_now_add=True) #------------------------------------------------------------------------------ class RecordVersion(db.Model): record = db.ReferenceProperty(Record) reference = db.StringProperty() header = db.TextProperty() data = db.TextProperty() version = db.StringProperty() previousVersionKey = db.StringProperty() previousVersion = db.SelfReferenceProperty() creation_date = db.DateTimeProperty(auto_now_add=True) update_date = db.DateTimeProperty(auto_now_add=True) access_date = db.DateTimeProperty(auto_now_add=True) def update(self, someData): recordData = someData['record']; self.parent().reference = recordData['reference'] self.parent().data = recordData['data'] self.parent().version = recordData['version'] self.parent().update_date = datetime.datetime.now() recordVersionData = someData['currentRecordVersion']; self.reference = recordVersionData ['reference'] self.data = recordVersionData ['data'] self.version = recordVersionData ['version'] #self.previous_version = #recordVersionData ['previousVersion'] self.previous_version_key = recordVersionData ['previousVersionKey'] self.update_date = datetime.datetime.now() #------------------------------------------------------------------------------ class OneTimePassword(db.Model): user = db.ReferenceProperty(User) status = db.StringProperty() reference = db.StringProperty() keyValue = db.StringProperty() keyChecksum = db.StringProperty() data = db.TextProperty() version = db.StringProperty() creation_date = db.DateTimeProperty(auto_now_add=True) request_date = db.DateTimeProperty() usage_date = db.DateTimeProperty() def update(self, someParameters, aStatus): self.reference = someParameters['reference'] self.keyValue = someParameters['key'] self.keyChecksum = someParameters['keyChecksum'] self.data = someParameters['data'] self.version = someParameters['version'] self.status = aStatus def reset(self, aStatus): self.data = "" self.status = aStatus return self #------------------------------------------------------------------------------ class Session(db.Expando): sessionId = db.StringProperty() access_date = db.DateTimeProperty() #============================================================================== class MainPage(webapp.RequestHandler): def get(self): path = os.path.join(os.path.dirname(__file__), 'static%s' % self.request.path) self.response.out.write(template.render(path, {})) #============================================================================== class XHR(webapp.RequestHandler): #========================================================================== def get(self): logging.info("self.request.path: " + self.request.path) if self.request.path == "/dump": session = self.getSession() userData = {} offline_data_placeholder = "" user = db.Query(User).filter('username =', session.C).get() userData['users'] = { 'catchAllUser': { '__masterkey_test_value__': 'masterkey', 's': '112233445566778899aabbccddeeff00112233445566778899aabbccddeeff00', 'v': '112233445566778899aabbccddeeff00112233445566778899aabbccddeeff00' } } records = {} for currentRecord in db.Query(Record).ancestor(user): versions = {} for currentVersion in db.Query(RecordVersion).ancestor(currentRecord): versions[currentVersion.reference] ={ 'header': currentVersion.header, 'data': currentVersion.data, 'version': currentVersion.version, 'creationDate': str(currentVersion.creation_date), 'updateDate': str(currentVersion.update_date), 'accessDate': str(currentVersion.access_date) } records[currentRecord.reference] = { 'data': currentRecord.data, 'version': currentRecord.version, 'creationDate': str(currentRecord.creation_date), 'updateDate': str(currentRecord.update_date), 'accessDate': str(currentRecord.access_date), 'currentVersion': currentVersion.reference, 'versions': versions } userData['users'][user.username] = { 's': user.srp_s, 'v': user.srp_v, 'version': user.auth_version, 'maxNumberOfRecords': '100', 'userDetails': user.header, 'statistics': user.statistics, 'userDetailsVersion': user.version, 'records': records } offline_data_placeholder = offline_data_placeholder + "_clipperz_dump_data_ = " + simplejson.dumps(userData, indent=4) + "\n" offline_data_placeholder = offline_data_placeholder + "Clipperz.PM.Proxy.defaultProxy = new Clipperz.PM.Proxy.Offline();" + "\n" offline_data_placeholder = offline_data_placeholder + "Clipperz.Crypto.PRNG.defaultRandomGenerator().fastEntropyAccumulationForTestingPurpose();" + "\n" path = os.path.join(os.path.dirname(__file__), 'static/dump.html') self.response.headers.add_header('Content-Type', 'text/html') self.response.headers.add_header('Content-Disposition', 'attachment', filename='Clipperz.html') self.response.out.write(template.render(path, {'offline_data_placeholder': offline_data_placeholder})) #========================================================================== def post(self): method = self.request.get('method') parameters = simplejson.loads(self.request.get('parameters')) session = self.getSession() result = {}; #---------------------------------------------------------------------- if method == 'registration': message = parameters['message']; if message == 'completeRegistration': user = User() user.updateCredentials(parameters['credentials']) user.update(parameters['user']) user.put() result['lock'] = user.lock result['result'] = "done" #---------------------------------------------------------------------- elif method == 'handshake': srp_g = 2L srp_n = long("0x%s" % "115b8b692e0e045692cf280b436735c77a5a9e8a9e7ed56c965f87db5b2a2ece3", 16) message = parameters['message']; #------------------------------------------------------------------ if message == 'connect': session.C = parameters['parameters']['C'] session.A = parameters['parameters']['A'] user = db.Query(User).filter('username =', session.C).get() if user != None: try: optId = session.otpId oneTimePassword = db.Query(OneTimePassword).filter('keyValue =', optId).get() if oneTimePassword.parent().username != user.username: oneTimePassword.reset('DISABLED').put() raise Exception, "User missmatch between the current session and 'One Time Password' user" elif oneTimePassword.status != 'REQUESTED': oneTimePassword.reset('DISABLED').put() raise Exception, "Tring to use an 'One Time Password' in the wrong state" oneTimePassword.reset("USED").put() result['oneTimePassword'] = oneTimePassword.reference except Exception, detail: logging.error("connect.optId: " + str(detail)) session.s = user.srp_s session.v = user.srp_v else: session.s = "112233445566778899aabbccddeeff00112233445566778899aabbccddeeff00" session.v = "112233445566778899aabbccddeeff00112233445566778899aabbccddeeff00" session.b = randomSeed() session.B = hex(long("0x%s" % session.v, 16) + pow(srp_g, long("0x%s" %session.b, 16), srp_n))[2:-1] result['s'] = session.s result['B'] = session.B #------------------------------------------------------------------ elif message == 'credentialCheck': B = long("0x%s" % session.B, 16) b = long("0x%s" % session.b, 16) A = long("0x%s" % session.A, 16) v = long("0x%s" % session.v, 16) u = long("0x%s" % clipperzHash(str(B)), 16) n = srp_n S = pow((A * pow(v, u, n)), b, n) K = clipperzHash(str(S)) M1 = clipperzHash(str(A) + str(B) + K) if M1 == parameters['parameters']['M1']: session.K = K M2 = clipperzHash(str(A) + M1 + K) result['M2'] = M2 result["connectionId"] = "" result["loginInfo"] = {} result["loginInfo"]["latest"] = {} result["loginInfo"]["current"] = {} result["offlineCopyNeeded"] = "false"; result["lock"] = "----"; else: result['error'] = "?" #------------------------------------------------------------------ elif message == 'oneTimePassword': oneTimePassword = db.Query(OneTimePassword).filter("keyValue =", parameters["parameters"]["oneTimePasswordKey"]).get() if oneTimePassword != None: if oneTimePassword.status == 'ACTIVE': if oneTimePassword.keyChecksum == parameters['parameters']['oneTimePasswordKeyChecksum']: #session.userId = str(oneTimePassword.parent().username) session.otpId = str(oneTimePassword.keyValue) result['data'] = oneTimePassword.data result['version'] = oneTimePassword.version oneTimePassword.reset('REQUESTED').put() else: oneTimePassword.reset('DISABLED').put() raise Exception, "The requested One Time Password has been disabled, due to a wrong keyChecksum" else: raise Exception, "The requested One Time Password was not active" else: raise Exception, "The requested One Time Password has not been found" #---------------------------------------------------------------------- elif method == 'message': if parameters['srpSharedSecret'] == session.K: message = parameters['message'] if message == 'getUserDetails': # {"message":"getUserDetails", "srpSharedSecret":"f18e5cf7c3a83b67d4db9444af813ee48c13daf4f8f6635397d593e52ba89a08", "parameters":{}} user = db.Query(User).filter('username =', session.C).get() result['header'] = user.header; result['statistics'] = user.statistics; result['version'] = user.version; elif message == "addNewRecords": user = db.Query(User).filter('username =', session.C).get() result = db.run_in_transaction(self.addNewRecords, session, user, parameters) """ user = db.Query(User).filter('username =', session.C).get() user.update(parameters['parameters']['user']) for recordParameter in parameters['parameters']['records']: record = Record(parent=user) record.put() recordVersion = RecordVersion(parent=record) recordVersion.put() recordVersion.update(recordParameter) record.put() recordVersion.put() user.put(); result['lock'] = user.lock result['result'] = 'done' """ elif message == 'getRecordDetail': record = db.Query(Record).ancestor(db.Query(User).filter('username =', session.C).get()).filter('reference =', parameters["parameters"]["reference"]).get() recordVersion = db.Query(RecordVersion).ancestor(record).get() result['currentVersion'] = {} result['currentVersion']['reference'] = recordVersion.reference result['currentVersion']['data'] = recordVersion.data result['currentVersion']['header'] = recordVersion.header result['currentVersion']['version'] = recordVersion.version result['currentVersion']['creationDate'] = str(recordVersion.creation_date) result['currentVersion']['updateDate'] = str(recordVersion.update_date) result['currentVersion']['accessDate'] = str(recordVersion.access_date) result['reference'] = record.reference result['data'] = record.data result['version'] = record.version result['creationDate'] = str(record.creation_date) result['updateDate'] = str(record.update_date) result['accessDate'] = str(record.access_date) result['oldestUsedEncryptedVersion'] = "---" elif message == 'updateData': user = db.Query(User).filter('username =', session.C).get() user.update(parameters['parameters']['user']) for recordParameter in parameters['parameters']['records']: logging.info('reference =' + recordParameter['record']['reference']) record = db.Query(Record).ancestor(user).filter('reference =', recordParameter['record']['reference']).get() recordVersion = db.Query(RecordVersion).ancestor(record).get() recordVersion.update(recordParameter) recordVersion.put() recordVersion.parent().put() user.put(); result['lock'] = user.lock result['result'] = 'done' elif message == 'deleteRecords': user = db.Query(User).filter('username =', session.C).get() user.update(parameters['parameters']['user']) for recordReference in parameters['parameters']['recordReferences']: record = db.Query(Record).ancestor(user).filter('reference =', recordReference).get() #recordVersion = db.Query(RecordVersion).ancestor(record).get() db.delete(db.Query(RecordVersion).ancestor(record)) record.delete() user.put() result['lock'] = user.lock result['result'] = 'done' elif message == 'deleteUser': user = db.Query(User).filter('username =', session.C).get() db.delete(db.Query(RecordVersion).ancestor(user)) db.delete(db.Query(Record).ancestor(user)) user.delete() elif message == 'addNewOneTimePassword': user = db.Query(User).filter('username =', session.C).get() user.update(parameters['parameters']['user']) oneTimePassword = OneTimePassword(parent=user) oneTimePassword.update(parameters['parameters']['oneTimePassword'], "ACTIVE") oneTimePassword.put() user.put() result['lock'] = user.lock result['result'] = 'done' elif message == 'updateOneTimePasswords': user = db.Query(User).filter('username =', session.C).get() user.update(parameters['parameters']['user']) validOtpReferences = parameters['parameters']['oneTimePasswords'] for currentOtp in db.Query(OneTimePassword).ancestor(user): if currentOtp.reference in validOtpReferences: pass else: currentOtp.delete() user.put() result['result'] = user.lock elif message == 'getOneTimePasswordsDetails': pass elif message == 'getLoginHistory': result["result"] = [] elif message == 'upgradeUserCredentials': user = db.Query(User).filter('username =', session.C).get() user.updateCredentials(parameters['parameters']['credentials']) user.update(parameters['parameters']['user']) for oneTimePasswordReference in parameters['parameters']['oneTimePasswords']: oneTimePassword = db.Query(OneTimePassword).ancestor(user).filter("reference =", oneTimePasswordReference).get() if oneTimePassword != None: oneTimePassword.data = parameters['parameters']['oneTimePasswords'][oneTimePasswordReference] oneTimePassword.put() user.put() result['lock'] = user.lock result['result'] = 'done' """ $user = new user(); $user->Get($_SESSION["userId"]); $otp = new onetimepassword(); updateUserCredentials($parameters["parameters"]["credentials"], $user); updateUserData($parameters["parameters"]["user"], $user); $otpList = $parameters["parameters"]["oneTimePasswords"]; foreach($otpList as $otpReference=>$otpData) { $otpList = $otp->GetList(array(array("reference", "=", $otpReference))); $currentOtp = $otpList[0]; $currentOtp->data = $otpData; $currentOtp->Save(); } $user->Save(); $result["lock"] = $user->lock; $result["result"] = "done"; """ #============================================================= """ java.util.Map result; try { java.util.Map credentials; if (someParameters.get("credentials") != null) { credentials = (java.util.Map)someParameters.get("credentials"); } else { credentials = someParameters; } aUser.setUsername((java.lang.String)credentials.get("C")); aUser.setSrpS((java.lang.String)credentials.get("s")); aUser.setSrpV((java.lang.String)credentials.get("v")); aUser.setVersion((java.lang.String)credentials.get("version")); if (someParameters.get("user") != null) { com.clipperz.dataModel.EncoderHelper.updateWithMap(aUser, (java.util.Map)someParameters.get("user")); } if (someParameters.get("oneTimePasswords") != null) { java.util.Map updatedOneTimePasswords; java.util.List usersOneTimePasswords; int i,c; updatedOneTimePasswords = (java.util.Map)someParameters.get("oneTimePasswords"); usersOneTimePasswords = com.clipperz.dataModel.OneTimePassword.oneTimePasswordsForUser(this.user()); c = usersOneTimePasswords.size(); for (i=0; i>> getSession (%d) => %s" % (db.Query(Session).count(), str(map(lambda v: v.sessionId, db.Query(Session).fetch(100)))) ) result = None try: sessionId = self.request.cookies['sessionId'] except: sessionId = None #logging.info("wannabe sessionId: " + str(sessionId)) if sessionId != None: #query = db.Query(Session) #query.filter('sessionId =', sessionId) #result = query.get() #result = db.Query(Session).filter('sessionId =', str(sessionId)).filter('access_date >', (datetime.datetime.utcnow() - sessionTimeout)).get() result = db.Query(Session).filter('sessionId =', str(sessionId)).get() #logging.info("searching session on datastore. Found: " + str(result)) if result == None: sessionId = str(uuid.uuid4()) #logging.info("creating a new session with sessionId=" + str(sessionId)) result = Session(sessionId=sessionId) result.access_date = datetime.datetime.utcnow() result.put() #logging.info("<<< getSession (%d)" % db.Query(Session).count()) return result #========================================================================== def saveSession(self, aSession): #logging.info(">>> saveSession (%d)" % db.Query(Session).count()) #self.response.set_cookie('sessionId', aSession.sessionId, max_age=360, path='/', domain='example.org', secure=True) aSession.put() self.response.headers.add_header('Set-Cookie', 'sessionId=' + str(aSession.sessionId), path='/') self.cleanOldSessions() #logging.info("<<< saveSession (%d)" % db.Query(Session).count()) #========================================================================== def cleanOldSessions(self): query = db.Query(Session).filter('accessDate <', (datetime.datetime.utcnow() - sessionTimeout)) expiredSessions = query.count(); if expiredSessions != 0: #logging.info("deleting %d sessions" % expiredSessions) pass """ try: db.delete(query) except Exception, exception: logging.error("some issues raised while deleting the expired sessions") logging.error("exception type: " + str(type(exception))) logging.error("exception: " + str(exception)) """ pass #============================================================================== def main(): application = webapp.WSGIApplication([('/xhr', XHR), ('/dump', XHR), ('/.*', MainPage)], debug=True) wsgiref.handlers.CGIHandler().run(application) if __name__ == "__main__": main()