summaryrefslogtreecommitdiff
path: root/zuul/lib/encryption.py
blob: 5720b9ceef3c19e9dae6fc1f3bd6f0de41fb0dbd (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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# Copyright 2017 Red Hat, Inc.
#
# 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.

from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives import hashes
from functools import lru_cache


# https://cryptography.io/en/latest/hazmat/primitives/asymmetric/rsa/#generation
def generate_rsa_keypair():
    """Generate an RSA keypair.

    :returns: A tuple (private_key, public_key)

    """
    private_key = rsa.generate_private_key(
        public_exponent=65537,
        key_size=4096,
        backend=default_backend()
    )
    public_key = private_key.public_key()
    return (private_key, public_key)


# https://cryptography.io/en/latest/hazmat/primitives/asymmetric/rsa/#key-serialization
def serialize_rsa_private_key(private_key, password=None):
    """Serialize an RSA private key

    This returns a PEM-encoded serialized form of an RSA private key
    suitable for storing on disk. In case a password is supplied the
    encoded key will be encrypted.

    :arg private_key: A private key object as returned by
        :func:generate_rsa_keypair()
    : arg password: A password in case the key should be encrypted

    :returns: A PEM-encoded string representation of the private key.

    """
    if password is None:
        encryption_algorithm = serialization.NoEncryption()
    else:
        encryption_algorithm = serialization.BestAvailableEncryption(password)

    return private_key.private_bytes(
        encoding=serialization.Encoding.PEM,
        format=serialization.PrivateFormat.TraditionalOpenSSL,
        encryption_algorithm=encryption_algorithm
    )


def serialize_rsa_public_key(public_key):
    """Serialize an RSA public key

    This returns a PEM-encoded serialized form of an RSA public key
    suitable for distribution.

    :arg public_key: A pubilc key object as returned by
        :func:generate_rsa_keypair()

    :returns: A PEM-encoded string representation of the public key.

    """
    return public_key.public_bytes(
        encoding=serialization.Encoding.PEM,
        format=serialization.PublicFormat.SubjectPublicKeyInfo
    )


# https://cryptography.io/en/latest/hazmat/primitives/asymmetric/rsa/#key-loading
def deserialize_rsa_keypair(data, password=None):
    """Deserialize an RSA private key

    This deserializes an RSA private key and returns the keypair
    (private and public) for use in decryption. If the given key
    is encrypted a password must be supplied.

    :arg data: A PEM-encoded serialized private key
    :arg password: Optional password in case the private key is encrypted.

    :returns: A tuple (private_key, public_key)

    """
    private_key = serialization.load_pem_private_key(
        data,
        password=password,
        backend=default_backend()
    )
    public_key = private_key.public_key()
    return (private_key, public_key)


# https://cryptography.io/en/latest/hazmat/primitives/asymmetric/rsa/#decryption
# lru_cache performs best if maxsize is a power of two
@lru_cache(maxsize=1024)
def decrypt_pkcs1_oaep(ciphertext, private_key):
    """Decrypt PKCS#1 (RSAES-OAEP) encoded ciphertext

    :arg ciphertext: A string previously encrypted with PKCS#1
        (RSAES-OAEP).
    :arg private_key: A private key object as returned by
        :func:generate_rsa_keypair()

    :returns: The decrypted form of the ciphertext as a string.

    """
    return private_key.decrypt(
        ciphertext,
        padding.OAEP(
            mgf=padding.MGF1(algorithm=hashes.SHA1()),
            algorithm=hashes.SHA1(),
            label=None
        )
    )


# https://cryptography.io/en/latest/hazmat/primitives/asymmetric/rsa/#encryption
def encrypt_pkcs1_oaep(plaintext, public_key):
    """Encrypt data with PKCS#1 (RSAES-OAEP)

    :arg plaintext: A string to encrypt with PKCS#1 (RSAES-OAEP).

    :arg public_key: A public key object as returned by
        :func:generate_rsa_keypair()

    :returns: The encrypted form of the plaintext.

    """
    return public_key.encrypt(
        plaintext,
        padding.OAEP(
            mgf=padding.MGF1(algorithm=hashes.SHA1()),
            algorithm=hashes.SHA1(),
            label=None
        )
    )