pgadmin4/web/pgadmin/utils/crypto.py

125 lines
3.3 KiB
Python

##########################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2019, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
#########################################################################
"""This File Provides Cryptography."""
from __future__ import division
import base64
import hashlib
import os
import six
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.ciphers import Cipher
from cryptography.hazmat.primitives.ciphers.algorithms import AES
from cryptography.hazmat.primitives.ciphers.modes import CFB8
padding_string = b'}'
iv_size = AES.block_size // 8
def encrypt(plaintext, key):
"""
Encrypt the plaintext with AES method.
Parameters:
plaintext -- String to be encrypted.
key -- Key for encryption.
"""
iv = os.urandom(iv_size)
cipher = Cipher(AES(pad(key)), CFB8(iv), default_backend())
encryptor = cipher.encryptor()
# If user has entered non ascii password (Python2)
# we have to encode it first
if isinstance(plaintext, six.text_type):
plaintext = plaintext.encode()
return base64.b64encode(iv + encryptor.update(plaintext) +
encryptor.finalize())
def decrypt(ciphertext, key):
"""
Decrypt the AES encrypted string.
Parameters:
ciphertext -- Encrypted string with AES method.
key -- key to decrypt the encrypted string.
"""
ciphertext = base64.b64decode(ciphertext)
iv = ciphertext[:iv_size]
cipher = Cipher(AES(pad(key)), CFB8(iv), default_backend())
decryptor = cipher.decryptor()
return decryptor.update(ciphertext[iv_size:]) + decryptor.finalize()
def pad(key):
"""Add padding to the key."""
if isinstance(key, six.text_type):
key = key.encode()
# Key must be maximum 32 bytes long, so take first 32 bytes
key = key[:32]
# If key size is 16, 24 or 32 bytes then padding is not required
if len(key) in (16, 24, 32):
return key
# Add padding to make key 32 bytes long
return key.ljust(32, padding_string)
def pqencryptpassword(password, user):
"""
pqencryptpassword -- to encrypt a password
This is intended to be used by client applications that wish to send
commands like ALTER USER joe PASSWORD 'pwd'. The password need not
be sent in cleartext if it is encrypted on the client side. This is
good because it ensures the cleartext password won't end up in logs,
pg_stat displays, etc. We export the function so that clients won't
be dependent on low-level details like whether the enceyption is MD5
or something else.
Arguments are the cleartext password, and the SQL name of the user it
is for.
Return value is "md5" followed by a 32-hex-digit MD5 checksum..
Args:
password:
user:
Returns:
"""
m = hashlib.md5()
# Place salt at the end because it may be known by users trying to crack
# the MD5 output.
# Handling of non-ascii password (Python2)
if hasattr(str, 'decode'):
password = password.encode('utf-8')
user = user.encode('utf-8')
else:
password = password.encode()
user = user.encode()
m.update(password)
m.update(user)
return "md5" + m.hexdigest()