1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
|
"""ONAP specific encryption-decryption for passwords"""
# org.onap.dcae
# ================================================================================
# Copyright (c) 2017 AT&T Intellectual Property. All rights reserved.
# ================================================================================
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============LICENSE_END=========================================================
#
# ECOMP is a trademark and service mark of AT&T Intellectual Property.
import base64
from Crypto.Cipher import AES
from Crypto.Protocol.KDF import PBKDF2
from Crypto import Random
class Cipher(object):
"""class for AES-256 encryption and decryption of text using the salted password"""
KEY_SIZE = 32 # AES-256
KDF_ITERATIONS = 16384
AES_MODE = AES.MODE_CFB
@staticmethod
def encrypt(password, plain_text):
"""
encrypt the :plain_text: into :cipher_text: using the password
:cipher_text: formatted as pbkdf2_salt + init_vector + encrypt(:plain_text:)
then cipher_text is encoded as base64 to make it textable (non-binary)
:pbkdf2_salt: has the fixed length of 32 (AES-256)
:init_vector: has the fixed length of AES.block_size
"""
pbkdf2_salt = Random.new().read(Cipher.KEY_SIZE)
init_vector = Random.new().read(AES.block_size)
derived_key = PBKDF2(password, pbkdf2_salt, Cipher.KEY_SIZE, Cipher.KDF_ITERATIONS)
cipher = AES.new(derived_key, Cipher.AES_MODE, init_vector)
cipher_text = base64.b64encode(pbkdf2_salt + init_vector + cipher.encrypt(plain_text))
return cipher_text
@staticmethod
def decrypt(password, cipher_text):
"""
decrypt the :cipher_text: into :plain_text: using the password
:cipher_text: is expected to be encoded as base64 to make it textable (non-binary)
inside of that it is expected to be formatted as
pbkdf2_salt + init_vector + encrypt(:plain_text:)
:pbkdf2_salt: has the fixed length of 32 (AES-256)
:init_vector: has the fixed length of AES.block_size
"""
cipher_text = base64.b64decode(cipher_text)
pbkdf2_salt = cipher_text[: Cipher.KEY_SIZE]
init_vector = cipher_text[Cipher.KEY_SIZE : Cipher.KEY_SIZE + AES.block_size]
cipher_text = cipher_text[Cipher.KEY_SIZE + AES.block_size :]
derived_key = PBKDF2(password, pbkdf2_salt, Cipher.KEY_SIZE, Cipher.KDF_ITERATIONS)
cipher = AES.new(derived_key, Cipher.AES_MODE, init_vector)
plain_text = cipher.decrypt(cipher_text).decode('utf-8')
return plain_text
|