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 send_secret_to_acs(sessionid): """ Send a freshly generated secret to ACS corresponding to the new AIC sessionid -- Session ID """ logger.info("Trying to add new user secret to ACS") # Sedn the session data to the ACS #XXX: Change to https is servers deployed with TLS url = "http://{}:{}/acs/user".format(acs_address, acs_port) logger.info("Adding secret via %s", url) import os secret = base64.urlsafe_b64encode(os.urandom(32)).decode('utf-8') payload = json.dumps({"id":sessionid, "twofactor":{"2FAKey":secret, "Counter":0}}) epayload = transport_security.construct_message(payload, srcprivkey=app_privatekey, destpubkey=acs_publickey) try: resp = requests.put(url, json=epayload) except requests.exceptions.Timeout: logger.warning("ACS request timeout") return False except requests.exceptions.ConnectionError: logger.warning("ACS connection refused") return False logger.info("ACS response code: %s", resp.status_code) if resp.status_code != 200: logger.warning("ACS rejected secret addition") return False return True
def validate_context(sessionid, context, extend_by): """ Contact auth and validate the users context sessionid -- ID of the session whose context is being validated context -- Context data in urlsafe_b64 format """ logger.info("Validating context via Auth for session: %s", sessionid) # Do a context validation call to the authentication server #XXX: Change to https when deployed with TLS url = "http://{}:{}/auth/{}/context".format(auth_address, auth_port, sessionid) # The payload contains the context data, and the time to extend the session by if the context is good payload = json.dumps({'context':context, 'extend_by':extend_by}) # Transport security secure_payload = transport_security.construct_message(payload, srcprivkey=app_privatekey, destpubkey=auth_publickey) try: # Do the request resp = requests.put(url, json=secure_payload) except requests.exceptions.Timeout: logger.warning("Auth request timeout") return False except requests.exceptions.ConnectionError: logger.warning("Auth connection refused") return False logger.info("Auth response code: %s", resp.status_code) # A 200 OK response means the context was validated successfully if resp.status_code != 200: logger.warning("Session %s context validation failed", sessionid) return False logger.info("Session %s context validation successful", sessionid) return True
def get_key(db, attribute): """ /key/<db>/<attribute> Get the key """ logger.info("Keys requested for %s/%s", db, attribute) # Each request must be verified by supplying a token try: token = flask.request.args.get('token') except KeyError: logger.warning("No token in request") flask.abort(401) logger.info("Authenticating token: %s", token) # Verify the HOPT token if not check_hotp_value(token): logger.warning("HOTP value is wrong") flask.abort(401) logger.info("Token %s authenticated", token) # Get the key key = retrieve_key(db, attribute) if not key: logger.warning("No key found for this combination") flask.abort(403) logger.info("Key for %s/%s retrieved", db, attribute) # Send the raw key, using transport_security resp = transport_security.construct_message(key, srcprivkey=app_privatekey, destpubkey=ags_publickey) return flask.jsonify(**resp)
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 serve_keys(db, attribute): """ /ags/access/<db>/<attribute>?id=<sessionid>&context=<contextdata> From user, give them the keys """ logger.info("GETting keys for %s/%s", db, attribute) # Check if request contains ID and context data try: sessionid = flask.request.args.get('id') contextdata = flask.request.args.get('context') if not sessionid or not contextdata: raise KeyError except KeyError: logger.warning("No id/context in URL") flask.abort(random_http_error()) # Context was a base64 encoded JSON string, turn it into a dictionary print(contextdata) newcontext = json.loads(base64.urlsafe_b64decode(contextdata).decode('utf-8')) # Check if session exists if not red.exists("session:"+sessionid): logger.warning("User not authorized by AS") flask.abort(random_http_error()) # Check policy before handing out keys role = red.hget("session:"+sessionid, "role").decode('utf-8') r, w = retrieve_policy(role, db, attribute) if not (r or w): logger.warning("User can't read or write %s/%s, not sending him keys", db, attribute) flask.abort(random_http_error()) # Context validation valid = validate_context(sessionid, context=newcontext, extend_by=key_lifetime) if not valid: # Context change is not valid, user needs to be logged out logger.warning("User context was not valid") if red.delete("session:"+sessionid): logger.warning("User logged out at AGS") else: logger.warning("Logout at AGS failed") flask.abort(random_http_error()) logger.info("User context is valid") # Key to send, retrieved from Key Server key = retrieve_keys(db, attribute) inner_payload = json.dumps({"key": key, "validity": key_lifetime}) #inner_payload = plaintext json # Inner layer of encryption using bindkey bindkey = red.hget('session:'+sessionid, "bindkey") fern = Fernet(bindkey) inner_payload = fern.encrypt(bytes(inner_payload, 'utf-8')).decode('utf-8') # Outer layer of encryption from transport_security # Load users public key from the In-memory database userpubkeystr = red.hget("session:"+sessionid, "pubkey") userpubkey = load_pem_public_key(userpubkeystr, backend=default_backend()) # Secure using transport_security outer_payload = transport_security.construct_message(inner_payload, srcprivkey=app_privatekey, destpubkey=userpubkey) extend_aic_on_lease(sessionid) # Extend authorization period by key lifetime logger.info("Key distributed to %s for %s/%s", sessionid, db, attribute) return flask.jsonify(**outer_payload)
def send_aic(sessionid, role, bindkey, pubkey): """ Send an AIC to the ags, returns success value sessionid -- Anonymized Session ID for user role -- Role as retrieved from database bindkey -- As generated during login pubkey -- Users stored public key context -- Users login context """ # AIC construct aic = {"sessionid":sessionid, "role":role, "bindkey":bindkey, "pubkey":pubkey} message = json.dumps(aic) logger.info("Genrated AIC: %s", message) # Secure the payload payload = transport_security.construct_message(message, srcprivkey=app_privatekey, destpubkey=ags_publickey) # Calculate the current HOTP token from file with open(ags_secretfile, 'r') as f: agsdict = json.loads(f.read()) counter = agsdict['Counter'] tfapass = agsdict['2FAKey'] hotp = HOTP(bytes(tfapass, 'utf-8'), 6, hashes.SHA1(), backend=default_backend()) tfaval = hotp.generate(counter).decode('utf-8') # Do the reques to the AGS #XXX: Change to https for deployment url = "http://{}:{}/ags/authorized?token={}".format(ags_address, ags_port, tfaval) logger.info("Adding AIC to AGS: %s", url) try: resp = requests.put(url, json=payload) except requests.Timeout: logger.warning("AGS connection timeout") return None except requests.ConnectionError: logger.warning("AGS connection error") return None logger.info("AGS AIC PUT status code: %s", resp.status_code) # A 200 OK response means the request was successful if resp.status_code == 200: # Update the HOTP counter value counter += 1 agsdict['Counter'] = counter with open(ags_secretfile, 'w') as f: f.write(json.dumps(agsdict)) logger.info("AGS HOTP counter was incremented to %s", counter) # Return the validity as returned by the AGS return resp.json()['validity'] else: logger.warning("AGS rejected AIC") return None
def main(): message = sys.argv[1] #print("Message={}".format(message)) print("Message length is: {}".format(len(message))) eck1 = ec.generate_private_key(ec.SECP384R1(), default_backend()) eck2 = ec.generate_private_key(ec.SECP384R1(), default_backend()) tsct = transport_security.construct_message(plaintext=message, srcprivkey=eck1, destpubkey=eck2.public_key()) #print("TS={}".format(tsct)) print("Transport Security: {}".format(len(json.dumps(tsct)))) tsoh = (len(json.dumps(tsct))/len(message))*100 k = Fernet.generate_key() f = Fernet(k) fct = f.encrypt(bytes(message, 'utf-8')).decode('utf-8') #print("Fernet={}".format(fct)) print("Fernet: {}".format(len(fct))) foh = (len(fct)/len(message))*100 with open('bandwidth.out', 'a') as f: record = "{}\t{}\t{}\n".format(len(message), tsoh, foh) f.write(record)
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)