def main(): message = 'This is my benchmarking message; it should really be longer' e1 = time.time() #Time key generation: EC eck1 = ec.generate_private_key(ec.SECP384R1(), default_backend()) eck2 = ec.generate_private_key(ec.SECP384R1(), default_backend()) e2 = time.time() #Time key generation: RSA rsak = rsa.generate_private_key(public_exponent=65537, key_size=4096, backend=default_backend()) e3 = time.time() #Time Encryption: EC ecct = transport_security.construct_message(message, srcprivkey=eck1, destpubkey=eck2.public_key()) e4 = time.time() #Time Encryption: RSA rsact = transport_security.get_raw_encrypted(message, pubkey=rsak.public_key()) e5 = time.time() #Time Decryption: EC ecpt = transport_security.deconstruct_message(message_dict=ecct, destprivkey=eck2, srcpubkey=eck1.public_key()) e6 = time.time() if ecpt == message: print("EC Decryption successful") else: print("EC Decryption failed") #Time Decryption: RSA rsapt = transport_security.get_raw_decrypted(ciphertext=rsact, privkey=rsak) e7 = time.time() if ecpt == message: print("RSA Decryption successful") else: print("RSA Decryption failed") with open('timing.out', 'a') as f: record = "{}\t{}\t{}\t{}\t{}\t{}\n".format(e2-e1, e3-e2, e4-e3, e5-e4, e6-e5, e7-e6) f.write(record)
def login(self, password, context): """ Do login, using either saved user info or provided ones """ self._logger.info("Trying to login") self._bindkey = None # Reset our bind key # Do the login request to the auth server # XXX: Change to https if server is using TLS url = "http://{}:{}/auth/login/{}".format(self._authaddr, self._authport, self._userid) self._logger.info("Login via: %s", url) # Build payload nonce = self._generate_nonce() # Generate a random nonce # Convert Context dict to URLSafe Base64 context = base64.urlsafe_b64encode(bytes(json.dumps(context), "utf-8")) self._login_context = context # This is our login context for this session # Build the payload payload = json.dumps({"nonce": nonce, "password": password, "context": context.decode("utf-8")}) # Apply transport_security enc_payload = transport_security.construct_message( plaintext=payload, srcprivkey=self._privatekey, destpubkey=self._authpubkey ) self._logger.info("Sending payload to authentication server") # Do the request try: resp = requests.put(url=url, json=enc_payload) except requests.exceptions.ConnectionError: self._logger.error("Could not connect to authentication server") return False except requests.exceptions.Timeout: self._logger.error("Authentication server connection timed out") return False # Check the response code if resp.status_code != 200: self._logger.error("Server response status is negative: %s", resp.status_code) return False self._logger.info("Server response is positive") # Decrypt the secure message enc_resp = transport_security.deconstruct_message( resp.json(), destprivkey=self._privatekey, srcpubkey=self._authpubkey ) # Load into a dict stuff = json.loads(enc_resp) if not all(k in stuff for k in ["session", "authenticated", "validity", "token"]): self._logger.error("Server response is mangled") return False # Unpack the bindkey self._bindkey = self._unpack_bindkey(nonce=nonce, token=stuff["token"]) if not self._bindkey: self._logger.error("Could not unpack bindkey") return False # Calculate the validity and expiry time of session self._logger.error("Unpacked bindkey; Validity=%s", stuff["validity"]) self._bindkey_expires = time.time() + stuff["validity"] # Session confirmed, store the session data self._current_session = stuff["session"] self._access_keys = {} self._logger.info("Session info: %s", self._current_session) self._logger.info("User successfully logged in") return True
def retrieve_keys(db, attribute): """ Retrieve keys from the KS; This involves querying the KS with an authentication token Then decrypting and returning the encrypted payload db -- Databse whose keys to retrieve attribute -- The attribute whose encryption key to retrieve """ # Load the HOTP secret and counter from the secret file of the Key Server with open(ks_secretfile, 'r') as f: ksdict = json.loads(f.read()) counter = ksdict['Counter'] tfapass = ksdict['2FAKey'] # Calculate the HOTP token value hotp = HOTP(bytes(tfapass, 'utf-8'), 6, hashes.SHA1(), backend=default_backend()) tfaval = hotp.generate(counter).decode('utf-8') # Do the request to the key server #XXX: Chnage to https for deployment url = "http://{}:{}/key/{}/{}?token={}".format(ks_address, ks_port, db, attribute, tfaval) logger.info("Trying to access key from KS: %s", url) # Do the request try: resp = requests.get(url) except requests.exceptions.Timeout: logger.warning("KS request timeout") return None except requests.exceptions.ConnectionError: logger.warning("KS connection refused") return None logger.info("KS response code: %s", resp.status_code) if resp.status_code == 200: # Got 200 means KS incremented the counter counter += 1 # Increment our own counter # Save the updated secret ksdict['Counter'] = counter with open(ks_secretfile, 'w') as f: f.write(json.dumps(ksdict)) logger.info("KS HOTP counter was incremented to %s", counter) # Get the transport secure message secured_message = resp.json() key = transport_security.deconstruct_message(secured_message, destprivkey=app_privatekey, srcpubkey=ks_publickey) # Check if decryption was successful if not key: logger.warning("Secured message from KS was not decrypted properly") return None return key else: # KS did not return the key logger.warning("KS replied negatively. Something is wrong.") return None
def request_access(self, db, attribute, newcontext=None): """ Request keys """ self._logger.info("Trying to retrieve keys for %s/%s", db, attribute) # If the user wants to reset their context, do that else use the login context if not newcontext: newcontext = self._login_context newcontext = base64.urlsafe_b64encode(bytes(json.dumps(newcontext), "utf-8")) # Do the request # XXX: Change to https if server is using TLS url = "http://{}:{}/ags/access/{}/{}?id={}&context={}".format( self._agsaddr, self._agsport, db, attribute, self._current_session["id"], newcontext.decode("utf-8") ) self._logger.info("Access via: %s", url) try: resp = requests.get(url=url) except requests.exceptions.ConnectionError: self._logger.error("Could not connect to grant server") return False except requests.exceptions.Timeout: self._logger.error("Grant server connection timed out") return False if resp.status_code != 200: self._logger.error("Grant server responded negatively") return False payload = resp.json() # Get rid of transport security actual_payload = transport_security.deconstruct_message( payload, destprivkey=self._privatekey, srcpubkey=self._agspubkey ) # Get rid of binding encryption fern = Fernet(self._bindkey) try: decrypted_payload = fern.decrypt(bytes(actual_payload, "utf-8")) except: self._logger.error("Could not decrypt binding encryption") return False # Load into dict, must contain key and validity payload = json.loads(decrypted_payload.decode("utf-8")) if not all(k in payload for k in ["key", "validity"]): self._logger.error("Grant server respnse is mangled") return False self._logger.info("Successfully retrieved key") # Add the key to our local storage self._add_key(db, attribute, payload["key"], payload["validity"]) self._logger.info("Keys added") return True
def add_authorization(): """ /ags/authorized From AS, Users Session got authorized """ logger.info("Adding authorization") # First we need to do token validation try: tfaval = flask.request.args.get('token') if not tfaval: raise KeyError except KeyError: logger.warning("No authorization token in URL") flask.abort(401) if not check_hotp_value(tfaval): logger.warning("HOTP token not authenticated") flask.abort(401) # Token validation successful, now we need to get the message logger.info("Token authenticated") secure_payload = flask.request.get_json() payload = transport_security.deconstruct_message(secure_payload, destprivkey=app_privatekey, srcpubkey=auth_publickey) if not payload: logger.warning("Payload could not be decrypted") flask.abort(400) payload = json.loads(payload) # A valid payload must contain all the following if not all(k in payload for k in ['sessionid', 'role', 'bindkey', 'pubkey']): logger.warning("Payload is incomplete") flask.abort(422) # Store the AIC contents in the In-memory database if not store_aic(payload['sessionid'], payload['pubkey'], payload['role'], payload['bindkey']): logger.warning("Public key format error; Could not store AIC") flask.abort(422) # The session info needs to be sent to the ACS # This part is only needed when the ACS is present #if not send_secret_to_acs(payload['sessionid']): # logger.warning("ACS did not accept user secret") # flask.abort(422) # Send back the validity resp = {"stored": True, "validity":aic_lifetime} logger.info("Sending back response: %s", resp) return flask.jsonify(**resp)
def context_check(sessionid): """ Verify the context of the session sessionid -- The session whose context is being checked """ logger.info("Context validation for %s", sessionid) # Get secure payload, decrypt it secure_payload = flask.request.get_json() payload = transport_security.deconstruct_message(secure_payload, srcpubkey=ags_publickey, destprivkey=app_privatekey) if not payload: logger.warning("Could not decrypt transport secure payload") flask.abort(403) # Load the JSON string into a dict payload = json.loads(payload) # Payload must contain all context and extension period if not all(k in payload for k in ['context', 'extend_by']): logger.warning("Incomplete payload") flask.abort(401) new_context = payload['context'] # Context suplied by user extend = payload['extend_by'] # Extrension specified by AGS # Check if session exists if not red.exists("session:"+sessionid): logger.warning("Context check for non-existant session") flask.abort(403) # Get old context and UserID, created during login old_context = red.hget("session:"+sessionid, key='context').decode('utf-8') userid = red.hget("session:"+sessionid, key='user').decode('utf-8') # Validate the users context, calling the context validation module valid = context_validator.validate(userid, old=old_context, new=new_context) if not valid: logger.warning("Context change validation failed") red.delete("session:"+sessionid) red.delete("user:"******"session:"+sessionid, extend) red.expire("user:"******"valid":True})
def main(): args = options() if args.command == 'encrypt': print("Encryption invoked, treating source key as private, destination key as public") with open(args.src, 'r') as f: pkbytes = bytes(f.read(), 'utf-8') if args.password: passbytes = bytes(args.password, 'utf-8') else: passbytes = None srcprivkey = load_pem_private_key(pkbytes, password=passbytes, backend=default_backend()) print("Loaded source private key from ", args.src) with open(args.dest, 'r') as f: pkbytes = bytes(f.read(), 'utf-8') destpubkey = load_pem_public_key(pkbytes, backend=default_backend()) print("Loaded destination public key from ", args.dest) with open(args.input, 'r') as f: plaintext = f.read() print("Loaded plaintext from ", args.input) ciphertext = transport_security.construct_message(plaintext, srcprivkey, destpubkey) print("Done with encryption") outfile = args.input + '.tsecure' if args.output: outfile = args.output print("Outputting to ", outfile) with open(outfile, 'w') as f: f.write(json.dumps(ciphertext)) print("Output complete") sys.exit(0) elif args.command == 'decrypt': print("Decryption invoked, treating source key as public, destination key as private") with open(args.dest, 'r') as f: pkbytes = bytes(f.read(), 'utf-8') if args.password: passbytes = bytes(args.password, 'utf-8') else: passbytes = None destprivkey = load_pem_private_key(pkbytes, password=passbytes, backend=default_backend()) print("Loaded destination private key from ", args.dest) with open(args.src, 'r') as f: pkbytes = bytes(f.read(), 'utf-8') srcpubkey = load_pem_public_key(pkbytes, backend=default_backend()) print("Loaded source public key from ", args.src) with open(args.input, 'r') as f: plaintext = f.read() print("Loaded ciphertext from ", args.input) ciphertext = transport_security.deconstruct_message(json.loads(plaintext), destprivkey, srcpubkey) if not ciphertext: print("Decryption failed", file=sys.stderr) sys.exit(1) print("Done with decryption") outfile = args.input + '.plain' if args.output: outfile = args.output print("Outputting to ", outfile) with open(outfile, 'w') as f: f.write(ciphertext) print("Output complete") sys.exit(0) else: print("We should never have come here") sys.exit(1)
def login_request(userid): """ /auth/login/<userid> Login a user userid -- The ID suplied by the user """ logger.info("%s trying to log in", userid) enc_payload = flask.request.get_json() # Retrieve the users info from the database user_info = get_userinfo_auth(userid=userid) if not user_info: logger.warning("User info not found") flask.abort(random_http_error()) logger.info("User info retrieved") # Check if already logged in if not bench and red.exists("user:"******"User trying to login while a session already exists") flask.abort(random_http_error()) # Decrypt login payload userpubkey = build_pubkey(user_info['pubkey']) payload = transport_security.deconstruct_message(enc_payload, srcpubkey=userpubkey, destprivkey=app_privatekey) if not payload: logger.warning("Could not decrypt transport secure message") flask.abort(random_http_error()) logger.info("Payload decrypted") # Turn JSON string to dict object payload = json.loads(payload) # Payload must contain all the below if not all(k in payload for k in ['nonce', 'password', 'context']): logger.warning("Incomplete payload") flask.abort(random_http_error()) # Users password is authenticated if not autenticate_user(password=payload['password'], pwhash=user_info['pwhash']): logger.warning("Wrong password from user") flask.abort(random_http_error()) logger.info("User %s successfully authenticated", userid) bindkey = generate_bindkey() # Generate unique session ID + key for this login session sessionid = generate_sessionid() sessionkey = generate_sessionkey() # If either could not be generated, we have run out of IDs/Keys if not sessionid or not sessionkey: logger.warning("Could not generate new session ID") flask.abort(random_http_error()) logger.info("Session ID generated for user %s", userid) # Send AIC, AGS returns the validity period of the stored AIC validity = send_aic(sessionid, role=user_info['role'], bindkey=bindkey, pubkey=user_info['pubkey']) logger.info("Generated AIC and Bindkey for user %s", userid) if not validity: logger.warning("AIC could not be sent to AGS") flask.abort(random_http_error()) logger.info("Sent AIC for %s to AGS", userid) # Add session to session list x = red.hmset("session:"+sessionid, {"user":userid, "key":sessionkey, "context":payload['context']}) if not x: logger.warning("Could not add session data to redis") flask.abort(500) # Expires after validity period red.expire("session:"+sessionid, validity) # Add user of list of users, who are logged in x = red.set("user:"******"Could not add session data to redis") flask.abort(500) logger.info("Set user %s to session %s", userid, sessionid) # Expires after validity period red.expire("user:"******"Users nonce is bad") flask.abort(random_http_error()) # Send response, contains session info, token, validity logger.info("Token generated for user %s", userid) resp = {"authenticated":True, "session":{"id":sessionid, "key":sessionkey}, "token":token, "validity":validity} respstr = json.dumps(resp) # Transport security enc_resp = transport_security.construct_message(respstr, srcprivkey=app_privatekey, destpubkey=userpubkey) return flask.jsonify(**enc_resp)