#
# Copyright 2008-2011 Clipperz Srl
#
# This file is part of Clipperz's Javascript Crypto Library.
# Javascript Crypto Library provides web developers with an extensive
# and efficient set of cryptographic functions. The library aims to
# obtain maximum execution speed while preserving modularity and
# reusability.
# For further information about its features and functionalities please
# refer to http://www.clipperz.com
#
# * Javascript Crypto Library 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.
#
# * Javascript Crypto Library 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 Javascript Crypto Library. If not, see
# .
#
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()