1

So I'm using the code from this SO answer to create a 2-way alphanumeric encryption function.

Basically, the functions look like this:

from Crypto import Random
from Crypto.Cipher import AES

from binascii import hexlify
from binascii import unhexlify

def encrypt_password(self, password):
    key = current_app.config['VAULT_KEY']
    iv = Random.new().read(AES.block_size)
    cipher = AES.new(key.strip("\'"), AES.MODE_CFB, iv)
    self.password_encrypted = hexlify(iv + cipher.encrypt(password))

def decrypt_password(self):
    key = current_app.config['VAULT_KEY']
    encrypted = unhexlify(self.password_encrypted)
    cipher = AES.new(key.strip("\'"), AES.MODE_CFB, encrypted[:AES.block_size])
    return cipher.decrypt(encrypted)[AES.block_size:]

These functions interact w/a database object that saves the result of encrypt_password; to decrypt, decrypt_password is used. (Pretty self-explanatory.)

I'm getting stuck on this line:

encrypted = unhexlify(self.password_encrypted)

Somehow, Python keeps telling me that self.password_encrypted isn't a hex digit. I have no idea what's going on b/c it's being saved to the object as a hex digit.

By the way, the string representation is: '\x[decimal numbers]' which I also think is strange. Shouldn't it be hexadecimal digits?

Traceback follows:

Traceback (most recent call last):
  File "/<>/venv/lib/python3.5/site-packages/flask/app.py", line 2000, in __call__
    return self.wsgi_app(environ, start_response)
  File "/<>/venv/lib/python3.5/site-packages/flask/app.py", line 1991, in wsgi_app
    response = self.make_response(self.handle_exception(e))
  File "/<>/venv/lib/python3.5/site-packages/flask/app.py", line 1567, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "/<>/venv/lib/python3.5/site-packages/flask/_compat.py", line 33, in reraise
    raise value
  File "/<>/venv/lib/python3.5/site-packages/flask/app.py", line 1988, in wsgi_app
    response = self.full_dispatch_request()
  File "/<>/venv/lib/python3.5/site-packages/flask/app.py", line 1641, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/<>/venv/lib/python3.5/site-packages/flask/app.py", line 1544, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/<>/venv/lib/python3.5/site-packages/flask/_compat.py", line 33, in reraise
    raise value
  File "/<>/venv/lib/python3.5/site-packages/flask/app.py", line 1639, in full_dispatch_request
    rv = self.dispatch_request()
  File "/<>/venv/lib/python3.5/site-packages/flask/app.py", line 1625, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "/<>/app/public/views.py", line 64, in test_queue
    return u.decrypt_password()
  File "/<>/app/models.py", line 80, in decrypt_password
    encrypted = unhexlify(self.password_encrypted)
binascii.Error: Non-hexadecimal digit found
Community
  • 1
  • 1
franklin
  • 1,800
  • 7
  • 32
  • 59
  • Do not encrypt password. Iterate over an HMAC with a random salt for about a 100ms duration (the salt needs to be saved with the hash). Use functions such as password_hash, PBKDF2, Bcrypt and similar functions. The point is to make the attacker spend a lot of time finding passwords by brute force. See OWASP (Open Web Application Security Project) [Password Storage Cheat Sheet](https://www.owasp.org/index.php/Password_Storage_Cheat_Sheet#Leverage_an_adaptive_one-way_function). – zaph Jul 10 '16 at 20:02
  • See [How to securely hash passwords, The Theory](http://security.stackexchange.com/questions/211/how-to-securely-hash-passwords/31846#31846) on Security Stackexchange. – zaph Jul 10 '16 at 20:03
  • @zaph I'm aware of the security risk of a 2-way alphanumeric encryption schema for passwords and I am aware that passwords should be Hashed. However, this is a unique case. The machine needs to be able to enter plain text passwords for automation purposes. If you have a way to design around that please let me know. – franklin Jul 10 '16 at 21:14
  • Unless you can keep the encryption key safe from an attacker who gains admin access there is little point in encryption the passwords. If the password is saved in a file it lacks such protection. To secure the encryption password you would need a HSM so that the key is only ever in memory or itself encrypted with a key in the HSM. – zaph Jul 10 '16 at 21:18
  • So you're saying that I should store the passwords as plain text? Granted this application is not serving super-sensitive data. Nor is it going to be deployed that widely. But, storing the password plain text just b/c I don't have access to an HSM seems at least a little unreasonable. – franklin Jul 10 '16 at 21:28
  • If the attacker **will not** gain admin rights to the system you can store the confidential information in plain text format outside the web root. If you assume the attacker **will** gain admin rights to the system, the usual assumption, the confidential information must be encrypted **and** the encryption key must not be stored in a file on the system. It the key is in a file the attacker will have it and the encryption will be pointless. What should be done if the information is confidential is to use a HSM to secure the key, they start at around $500 and go up rapidly. – zaph Jul 10 '16 at 21:36
  • 1
    Backing up: You first need to define the security threat model. What is the value of the data to you, your reputation, the user and the attacker. How determined will the attacker be and at what skill level. They you can start defining your security. Note: there is no 100% level of security. – zaph Jul 10 '16 at 21:40

1 Answers1

1

Okay so I made a few modifications to the file you posted...

 from Crypto import Random
 from Crypto.Cipher import AES 

 from binascii import hexlify, unhexlify

 import hashlib 
 from sys import stdout as puts

 class AESCipher:

      def __init__(self, key):
           self.key = key

      def encrypt_password(self, password):
         iv = Random.new().read(AES.block_size)
         cipher = AES.new(self.key, AES.MODE_CFB, iv)
         self.password_encrypted = hexlify(iv + cipher.encrypt(password))
         return self.password_encrypted

      def decrypt_password(self):
          decrypted = unhexlify(self.password_encrypted)
          cipher = AES.new(self.key, AES.MODE_CFB, decrypted[:AES.block_size])
          return cipher.decrypt(decrypted)[AES.block_size:]


#############################################################################
               """sample purposes only"""

"""Here's an example, you don't have to use stdout or the block size for encyrption 
   I decided to use the regular base 16 block size and for 'puts' I came from a 
   ruby background"""

retrive_password = AESCipher(hashlib.sha256("mylittlepony").digest())

puts.write(("\n"*3) + retrieve_password.encrypt_password("agenericpassword") + ("\n" *3))

puts.flush()

puts.write(retrive_password.decrypt_password() + ("\n" *3))

puts.flush(()

It may have something to do with your environment key variable, make sure it complies with the 16, 24, or 32 byte construct. for the AES initialization first parameter.

As to the '\x{0-9a-fA-f}{0-9a-fA-f}' format, to my understanding that is a hexadecimal output in a string format

akiespenc
  • 305
  • 3
  • 8