aboutsummaryrefslogtreecommitdiffstats
path: root/policyhandler/onap/crypto.py
blob: e2d58dbb6afe99c3e3bc5a364465dfe07711c215 (plain)
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