Example #1
0
 def _try_dbus_auth(self, restaurant_identifier: str) -> Optional[bytes]:
     eprint("Trying D-Bus Secret Service")
     try:
         with secretstorage.dbus_init() as connection:
             collection = secretstorage.get_default_collection(connection)
             attributes = {
                 "application": "Scone",
                 "restaurant": restaurant_identifier,
             }
             items = list(collection.search_items(attributes))
             if items:
                 eprint("Found secret sauce for this Restaurant, unlocking…")
                 items[0].unlock()
                 return URLSafeBase64Encoder.decode(items[0].get_secret())
             else:
                 eprint("Did not find secret sauce for this Restaurant.")
                 eprint("Enter it and I will try and store it...")
                 secret = self._try_manual_entry()
                 if secret is not None:
                     collection.create_item(
                         f"scone({restaurant_identifier}): secret sauce",
                         attributes,
                         URLSafeBase64Encoder.encode(secret),
                     )
                     return secret
                 return None
     except EOFError:  # XXX what happens with no D-Bus
         return None
Example #2
0
    def recv_msg(self, user, packet):
        '''
		INPUT
			* user		: user message is from 
			* packet	: encrypted message received
		OUTPUT
			* Decrypted message with appropriate 
		'''
        try:
            dec_packet = encoder.decode(packet)
            self.send_ratc[user] = PublicKey(dec_packet[:32])
            mac_packet = dec_packet[32:64]
            enc_msg = dec_packet[64:]
            dh_sec = get_DH_secret(self.recv_ratc[user], self.send_ratc[user])
            salt = hashlib.md5(dh_sec).digest()
            self.root_keys[user] = kdf(self.root_keys[user], slt=salt)
            box = SecretBox(self.root_keys[user])
            msg = box.decrypt(enc_msg)
            mac = hashlib.pbkdf2_hmac('sha256', msg, self.ad_number[user],
                                      1414)
            if mac == mac_packet:
                return msg
            else:
                raise Exception('VERIFICATION FAILED: Connection to [' + user +
                                '] aborted.')
        except Exception as e:
            print '[SALSAJAR: recv_msg] ' + str(e)
Example #3
0
    def generate_new(self):
        eprint("Generating a new freezer key...")
        self.key = nacl.utils.random(SecretBox.KEY_SIZE)
        key_b64 = URLSafeBase64Encoder.encode(self.key)
        eprint("Your new key is: " + key_b64.decode())
        eprint("Pretty please store it in a safe place!")

        if not self.restaurant_identifier:
            eprint("No RI; not saving to SS")
            return

        eprint("Attempting to save it to the secret service...")
        eprint("(save it yourself anyway!)")

        with secretstorage.dbus_init() as connection:
            collection = secretstorage.get_default_collection(connection)
            attributes = {
                "application": "Scone",
                "restaurant": self.restaurant_identifier,
            }
            items = list(collection.search_items(attributes))
            if items:
                eprint(
                    "Found secret sauce for this Restaurant already!"
                    " Will not overwrite."
                )
            else:
                eprint("Storing secret sauce for this Restaurant...")
                collection.create_item(
                    f"scone({self.restaurant_identifier}): secret sauce",
                    attributes,
                    key_b64,
                )
                eprint("OK!")
Example #4
0
 def _try_manual_entry(self) -> Optional[bytes]:
     eprint("Manual entry required. Enter password for this restaurant: ", end="")
     key = URLSafeBase64Encoder.decode(input().encode())
     if len(key) != SecretBox.KEY_SIZE:
         eprint("Wrong size!")
         return None
     else:
         return key
Example #5
0
    def get_follow_params(self, user):
        '''
		INPUT
			* user		: user that client is performing x3dh with
		OUTPUT
			* Saves used keys to dictionary specific to 'user' so that keys can be used again
			* generates new ephemeral keys for new requests
			* Returns keys necessary for x3dh_follow by user 
		'''
        tmp_pub = self.ek_pub
        tmp_priv = self.ek_priv
        self.used_keys[user] = tmp_priv
        self.ek_priv, self.ek_pub = gen_DH()
        return encoder.encode(self.ik_pub + tmp_pub)
Example #6
0
    def get_init_params(self, user):
        '''
		INPUT
			* user		: user that server is performing x3dh with
		OUTPUT
			* Saves used keys to dictionary specific to 'user' so that keys can be used again
			* generates new one-time pre-keys for new requests
			* Returns keys necessary for x3dh_init by user 
		'''
        tmp_pub = self.opk_pub
        tmp_priv = self.opk_priv
        self.used_keys[user] = tmp_priv
        self.opk_priv, self.opk_pub = gen_DH()
        return encoder.encode(self.sign_key.verify_key.encode() + self.ik_pub +
                              self.spk_pub + tmp_pub)
Example #7
0
    def create_msg(self, user, msg):
        '''
		INPUT
			* user		: user message is being sent to 
			* msg		: unencrypted message being sent
		OUTPUT
			* Encoded(DH_pub key + hmac(MtE) + Encrypted(msg))
		'''
        self.recv_ratc[user], pub_key = gen_DH()
        dh_sec = get_DH_secret(self.recv_ratc[user], self.send_ratc[user])
        salt = hashlib.md5(dh_sec).digest()
        self.root_keys[user] = kdf(self.root_keys[user], slt=salt)
        box = SecretBox(self.root_keys[user])
        enc_msg = box.encrypt(msg)
        mac = hashlib.pbkdf2_hmac('sha256', msg, self.ad_number[user], 1414)
        return encoder.encode(pub_key + mac + enc_msg)
Example #8
0
    def close(self, password, filename):

        salt = os.urandom(32)
        key = hashlib.pbkdf2_hmac('sha256', password, salt, 1414)
        out_filename = filename + '.LOCK'
        box = SecretBox(key)
        try:
            with open(filename, 'rb') as infile:
                content = infile.read()
            enc_content = box.encrypt(content)
            with open(out_filename, 'wb') as outfile:
                outfile.write(encoder.encode(salt + enc_content))
                outfile.close()
        except Exception as e:
            if hasattr(e, 'message'):
                print e.message()
            else:
                print e
Example #9
0
    def open(self, password, filename):

        if '.LOCK' == filename[len(filename) - 5:]:
            out_filename = filename[:len(filename) - 5] + '.UNLOCK'
        else:
            out_filename = filename + '.UNLOCK'
        try:
            with open(filename, 'rb') as infile:
                encoded_content = infile.read()
            decoded_content = encoder.decode(encoded_content)
            salt = decoded_content[:32]
            key = hashlib.pbkdf2_hmac('sha256', password, salt, 1414)
            box = SecretBox(key)
            dec_content = box.decrypt(decoded_content[32:])
            with open(out_filename, 'wb') as outfile:
                outfile.write(dec_content)
                outfile.close()
        except Exception as e:
            if hasattr(e, 'message'):
                print e.message()
            else:
                print e
Example #10
0
def renew_endpoint():
    """
    Renews a JWT that has not expired.
    """
    if request.method == 'POST':

        if not app.config["JWT"]:
            return jsonify(message="JWT renewal is not enabled"), 501

        request_json = request.get_json(force=True, cache=False)

        token       = request_json.get('jwt', None)
        username    = request_json.get('username', None)

        if token is None or username is None:
            return jsonify(message="No JWT or username provided"), 400

        sanitized_username = str(escape(username))

        if not validate_username(sanitized_username):
            structured_log(level='warning', msg="Invalid username provided", user=f"'{sanitized_username}'")
            return jsonify(message="Invalid username provided"), 400

        try:
            claims = jwt.get_unverified_claims(token)
        except JWTError:
            return jsonify(message="Invalid JWT"), 400

        try:
            subject = claims["sub"].encode('utf-8')
            salt    = claims["x"].encode('utf-8')
        except KeyError:
            return jsonify(message="Invalid claims in JWT"), 401

        if sanitized_username != claims["sub"]:
            return jsonify(message="Invalid subject in JWT claim"), 400

        master_key  = app.config["JWT_MASTER_KEY"]
        algorithm   = app.config["JWT_ALGORITHM"]
        issuer      = app.config['APP_NAME']

        # jwt.decode() requires secret_key to be a str, so it must be decoded
        secret_key = blake2b(b'', key=master_key, salt=salt, person=subject).decode('utf-8')

        # exception is raised if token has expired, signature verification fails, etc.
        try:
            payload = jwt.decode(token=token, key=secret_key, algorithms=algorithm, issuer=issuer)
        except Exception as err:
            structured_log(level='info', msg="Failed to renew JWT", error=err)
            return jsonify(message="Failed to renew invalid JWT"), 401

        issue_time  = time.time()
        expiry_time = issue_time + app.config['JWT_VALIDITY_PERIOD']

        salt = URLSafeBase64Encoder.encode(nacl.utils.random(12))

        payload['iat']  = issue_time
        payload['exp']  = expiry_time
        payload['x']    = salt.decode('utf-8')

        # compute a new secret key for the regenerated JWT
        new_secret_key  = blake2b(b'', key=master_key, salt=salt, person=subject).decode('utf-8')
        new_token       = jwt.encode(claims=payload, key=new_secret_key, algorithm=algorithm)

        statsd.client.incr("jwt_renewed")
        structured_log(level='info', msg="JWT successfully renewed", user="******".format(sanitized_username))
        return jsonify(message="JWT successfully renewed", jwt=new_token), 200
Example #11
0
def auth_endpoint():
    """
    Authenticates users using PAM.
    If authentication is successful, returns a list of groups the user is a member of.
    Returns a short-lived JSON Web Token if JWT_MASTER_KEY is set.
    """
    if request.method == 'POST':
        request_json = request.get_json(force=True, cache=False)

        username = request_json.get('username', None)
        password = request_json.get('password', None)

        if username is None or password is None:
            return jsonify(message="No username or password provided"), 400

        sanitized_username = str(escape(username))

        if not validate_username(sanitized_username):
            structured_log(level='warning', msg="Invalid username provided", user=f"'{sanitized_username}'")
            return jsonify(message="Invalid username provided"), 400

        pam_service = app.config['PAM_SERVICE']

        with statsd.client.timer("pam_auth"):
            if pam().authenticate(sanitized_username, password, service=pam_service):
                authenticated = True
                auth_message = "Authentication successful"
                statsd.client.incr("auth_success")
            else:
                authenticated = False
                auth_message = "Authentication failed"
                statsd.client.incr("auth_failed")

        structured_log(level='info', msg=auth_message, user=f"'{sanitized_username}'")

        if authenticated:
            groups = get_group_membership(sanitized_username)

            token = None

            if app.config["JWT"]:
                issuer      = app.config['APP_NAME']
                issue_time  = time.time()
                expiry_time = issue_time + app.config['JWT_VALIDITY_PERIOD']
                subject     = sanitized_username.encode('utf-8')

                # generate a unique salt for each JWT, needs to be encoded to include as a claim
                salt = URLSafeBase64Encoder.encode(nacl.utils.random(12))

                claims = {
                    "iss": issuer,
                    "iat": issue_time,
                    "exp": expiry_time,
                    "sub": sanitized_username,
                    "groups": groups,
                    "x": salt.decode('utf-8')
                }

                master_key  = app.config["JWT_MASTER_KEY"]
                algorithm   = app.config["JWT_ALGORITHM"]

                # generate a unique secret key for each JWT
                secret_key = blake2b(b'', key=master_key, salt=salt, person=subject).decode('utf-8')

                token = jwt.encode(claims=claims, key=secret_key, algorithm=algorithm)
                statsd.client.incr("jwt_generated")

            return jsonify(message=f"{auth_message}", auth=True, groups=groups, jwt=token), 200
        else:
            return jsonify(message=f"{auth_message}", auth=False), 401
def base64url_decode(string):
    return URLSafeBase64Encoder.decode(fix_base64url_decode(string))
def base64url_encode(data):
    return fix_base64url_encode(URLSafeBase64Encoder.encode(data))