summaryrefslogtreecommitdiff
path: root/leakcheck/crypto.py
blob: c99561083ad5aba33941787c07c6ed780282ddc7 (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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
# Copyright (C) Jean-Paul Calderone
# See LICENSE for details.

import sys

from OpenSSL.crypto import (
    FILETYPE_PEM,
    TYPE_DSA,
    Error,
    PKey,
    X509,
    load_privatekey,
    CRL,
    Revoked,
    get_elliptic_curves,
    _X509_REVOKED_dup,
)

from OpenSSL._util import lib as _lib


class BaseChecker(object):
    def __init__(self, iterations):
        self.iterations = iterations


class Checker_X509_get_pubkey(BaseChecker):
    """
    Leak checks for L{X509.get_pubkey}.
    """

    def check_exception(self):
        """
        Call the method repeatedly such that it will raise an exception.
        """
        for i in xrange(self.iterations):
            cert = X509()
            try:
                cert.get_pubkey()
            except Error:
                pass

    def check_success(self):
        """
        Call the method repeatedly such that it will return a PKey object.
        """
        small = xrange(3)
        for i in xrange(self.iterations):
            key = PKey()
            key.generate_key(TYPE_DSA, 256)
            for i in small:
                cert = X509()
                cert.set_pubkey(key)
                for i in small:
                    cert.get_pubkey()


class Checker_load_privatekey(BaseChecker):
    """
    Leak checks for :py:obj:`load_privatekey`.
    """

    ENCRYPTED_PEM = """\
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: BF-CBC,3763C340F9B5A1D0

a/DO10mLjHLCAOG8/Hc5Lbuh3pfjvcTZiCexShP+tupkp0VxW2YbZjML8uoXrpA6
fSPUo7cEC+r96GjV03ZIVhjmsxxesdWMpfkzXRpG8rUbWEW2KcCJWdSX8bEkuNW3
uvAXdXZwiOrm56ANDo/48gj27GcLwnlA8ld39+ylAzkUJ1tcMVzzTjfcyd6BMFpR
Yjg23ikseug6iWEsZQormdl0ITdYzmFpM+YYsG7kmmmi4UjCEYfb9zFaqJn+WZT2
qXxmo2ZPFzmEVkuB46mf5GCqMwLRN2QTbIZX2+Dljj1Hfo5erf5jROewE/yzcTwO
FCB5K3c2kkTv2KjcCAimjxkE+SBKfHg35W0wB0AWkXpVFO5W/TbHg4tqtkpt/KMn
/MPnSxvYr/vEqYMfW4Y83c45iqK0Cyr2pwY60lcn8Kk=
-----END RSA PRIVATE KEY-----
"""

    def check_load_privatekey_callback(self):
        """
        Call the function with an encrypted PEM and a passphrase callback.
        """
        for i in xrange(self.iterations * 10):
            load_privatekey(
                FILETYPE_PEM, self.ENCRYPTED_PEM, lambda *args: "hello, secret"
            )

    def check_load_privatekey_callback_incorrect(self):
        """
        Call the function with an encrypted PEM and a passphrase callback which
        returns the wrong passphrase.
        """
        for i in xrange(self.iterations * 10):
            try:
                load_privatekey(
                    FILETYPE_PEM,
                    self.ENCRYPTED_PEM,
                    lambda *args: "hello, public",
                )
            except Error:
                pass

    def check_load_privatekey_callback_wrong_type(self):
        """
        Call the function with an encrypted PEM and a passphrase callback which
        returns a non-string.
        """
        for i in xrange(self.iterations * 10):
            try:
                load_privatekey(
                    FILETYPE_PEM, self.ENCRYPTED_PEM, lambda *args: {}
                )
            except ValueError:
                pass


class Checker_CRL(BaseChecker):
    """
    Leak checks for L{CRL.add_revoked} and L{CRL.get_revoked}.
    """

    def check_add_revoked(self):
        """
        Call the add_revoked method repeatedly on an empty CRL.
        """
        for i in xrange(self.iterations * 200):
            CRL().add_revoked(Revoked())

    def check_get_revoked(self):
        """
        Create a CRL object with 100 Revoked objects, then call the
        get_revoked method repeatedly.
        """
        crl = CRL()
        for i in xrange(100):
            crl.add_revoked(Revoked())
        for i in xrange(self.iterations):
            crl.get_revoked()


class Checker_X509_REVOKED_dup(BaseChecker):
    """
    Leak checks for :py:obj:`_X509_REVOKED_dup`.
    """

    def check_X509_REVOKED_dup(self):
        """
        Copy an empty Revoked object repeatedly. The copy is not garbage
        collected, therefore it needs to be manually freed.
        """
        for i in xrange(self.iterations * 100):
            revoked_copy = _X509_REVOKED_dup(Revoked()._revoked)
            _lib.X509_REVOKED_free(revoked_copy)


class Checker_EllipticCurve(BaseChecker):
    """
    Leak checks for :py:obj:`_EllipticCurve`.
    """

    def check_to_EC_KEY(self):
        """
        Repeatedly create an EC_KEY* from an :py:obj:`_EllipticCurve`.  The
        structure should be automatically garbage collected.
        """
        curves = get_elliptic_curves()
        if curves:
            curve = next(iter(curves))
            for i in xrange(self.iterations * 1000):
                curve._to_EC_KEY()


def vmsize():
    return [x for x in file("/proc/self/status").readlines() if "VmSize" in x]


def main(iterations="1000"):
    iterations = int(iterations)
    for klass in globals():
        if klass.startswith("Checker_"):
            klass = globals()[klass]
            print klass
            checker = klass(iterations)
            for meth in dir(checker):
                if meth.startswith("check_"):
                    print "\t", meth, vmsize(), "...",
                    getattr(checker, meth)()
                    print vmsize()


if __name__ == "__main__":
    main(*sys.argv[1:])