gntplib 0.5__py3-none-any.whl → 0.66__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of gntplib might be problematic. Click here for more details.
- gntplib/__init__.py +973 -959
- gntplib/__version__.py +1 -0
- gntplib/async.py +193 -193
- gntplib/ciphers.py +97 -97
- gntplib/compat.py +9 -9
- gntplib/exceptions.py +5 -5
- gntplib/keys.py +79 -79
- gntplib/utils.py +11 -11
- {gntplib-0.5.dist-info → gntplib-0.66.dist-info}/METADATA +118 -120
- gntplib-0.66.dist-info/RECORD +12 -0
- {gntplib-0.5.dist-info → gntplib-0.66.dist-info}/WHEEL +5 -5
- gntplib-0.5.dist-info/RECORD +0 -11
- {gntplib-0.5.dist-info → gntplib-0.66.dist-info}/top_level.txt +0 -0
gntplib/__version__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
version = 0.63
|
gntplib/async.py
CHANGED
|
@@ -1,193 +1,193 @@
|
|
|
1
|
-
"""This module provides support for asynchronous processing built on Tornado_.
|
|
2
|
-
|
|
3
|
-
.. _Tornado: http://www.tornadoweb.org/
|
|
4
|
-
"""
|
|
5
|
-
|
|
6
|
-
from __future__ import unicode_literals
|
|
7
|
-
import logging
|
|
8
|
-
import socket
|
|
9
|
-
|
|
10
|
-
from tornado import gen, httpclient, ioloop, iostream
|
|
11
|
-
|
|
12
|
-
import gntplib
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
__all__ = ['AsyncIcon', 'AsyncNotifier', 'AsyncPublisher', 'AsyncResource',
|
|
16
|
-
'AsyncSubscriber']
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
logger = logging.getLogger(__name__)
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
class AsyncPublisher(gntplib.Publisher):
|
|
23
|
-
"""Asynchronous Publisher of Growl Notification Transport Protocol (GNTP).
|
|
24
|
-
|
|
25
|
-
Same as :class:`~gntplib.Publisher` except the following:
|
|
26
|
-
|
|
27
|
-
* uses :class:`AsyncGNTPClient` as `gntp_client_class`.
|
|
28
|
-
* accepts :class:`tornado.ioloop.IOLoop` instance by additional
|
|
29
|
-
`io_loop` keyword argument.
|
|
30
|
-
|
|
31
|
-
:param name: the name of the application.
|
|
32
|
-
:param event_defs: the definitions of the notifications.
|
|
33
|
-
:param icon: url string or an instance of :class:`Resource` for the icon of
|
|
34
|
-
the application. Defaults to `None`.
|
|
35
|
-
:param io_loop: :class:`tornado.ioloop.IOLoop` instance.
|
|
36
|
-
:param custom_headers: the list of key-value tuples for Custom Headers.
|
|
37
|
-
:param app_specific_headers: the list of key-value tuples for App-Specific
|
|
38
|
-
Headers.
|
|
39
|
-
"""
|
|
40
|
-
|
|
41
|
-
def __init__(self, name, event_defs, icon=None, io_loop=None, **kwargs):
|
|
42
|
-
io_loop = io_loop or ioloop.IOLoop.instance()
|
|
43
|
-
gntplib.Publisher.__init__(self, name, event_defs, icon,
|
|
44
|
-
gntp_client_class=AsyncGNTPClient,
|
|
45
|
-
io_loop=io_loop, **kwargs)
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
class AsyncNotifier(AsyncPublisher, gntplib.Notifier):
|
|
49
|
-
"""Deprecated Asynchronous Notifier of Growl Notification Transport
|
|
50
|
-
Protocol (GNTP)."""
|
|
51
|
-
|
|
52
|
-
def __init__(self, name, event_defs, icon=None, io_loop=None, **kwargs):
|
|
53
|
-
import warnings
|
|
54
|
-
warnings.warn('AsyncNotifier is deprecated, use AsyncPublisher'
|
|
55
|
-
' instead', DeprecationWarning, stacklevel=2)
|
|
56
|
-
AsyncPublisher.__init__(self, name, event_defs, icon, io_loop,
|
|
57
|
-
**kwargs)
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
class AsyncSubscriber(gntplib.Subscriber):
|
|
61
|
-
"""Asynchronous Subscriber of Growl Notification Transport Protocol (GNTP).
|
|
62
|
-
|
|
63
|
-
Same as :class:`~gntplib.Subscriber` except the following:
|
|
64
|
-
|
|
65
|
-
* uses :class:`AsyncGNTPClient` as `gntp_client_class`.
|
|
66
|
-
* accepts :class:`tornado.ioloop.IOLoop` instance by additional
|
|
67
|
-
`io_loop` keyword argument.
|
|
68
|
-
|
|
69
|
-
:param name: the name of the subscriber.
|
|
70
|
-
:param hub: the subscribed-to machine. If a string is given, it is used as
|
|
71
|
-
a host of the hub and default port number `23053` is used.
|
|
72
|
-
If host-port tuple is given, it is used directly.
|
|
73
|
-
:param password: the password of the subscribed-to machine.
|
|
74
|
-
:param id_: the unique id of the subscriber. If not set, randomly
|
|
75
|
-
generated UUID is used.
|
|
76
|
-
:param port: the port number of the subscriber. Defaults to `23053`.
|
|
77
|
-
:param io_loop: :class:`tornado.ioloop.IOLoop` instance.
|
|
78
|
-
:param custom_headers: the list of key-value tuples for Custom Headers.
|
|
79
|
-
:param app_specific_headers: the list of key-value tuples for App-Specific
|
|
80
|
-
Headers.
|
|
81
|
-
"""
|
|
82
|
-
|
|
83
|
-
def __init__(self, id_, name, hub, password, port=gntplib.DEFAULT_PORT,
|
|
84
|
-
io_loop=None, **kwargs):
|
|
85
|
-
io_loop = io_loop or ioloop.IOLoop.instance()
|
|
86
|
-
gntplib.Subscriber.__init__(self, id_, name, hub, password, port=port,
|
|
87
|
-
gntp_client_class=AsyncGNTPClient,
|
|
88
|
-
io_loop=io_loop, **kwargs)
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
class AsyncGNTPConnection(gntplib.BaseGNTPConnection):
|
|
92
|
-
"""Asynchronous GNTP connection built on Tornado."""
|
|
93
|
-
|
|
94
|
-
def __init__(self, address, timeout, final_callback, socket_callback=None,
|
|
95
|
-
io_loop=None):
|
|
96
|
-
gntplib.BaseGNTPConnection.__init__(self, final_callback,
|
|
97
|
-
socket_callback)
|
|
98
|
-
sock = socket.create_connection(address, timeout=timeout)
|
|
99
|
-
self.stream = iostream.IOStream(sock, io_loop=io_loop)
|
|
100
|
-
|
|
101
|
-
def write_message(self, message):
|
|
102
|
-
"""Send the request to the GNTP server."""
|
|
103
|
-
self.stream.write(message)
|
|
104
|
-
|
|
105
|
-
def read_message(self, callback):
|
|
106
|
-
"""Read a message from opened stream and run callback with it."""
|
|
107
|
-
self.stream.read_until(gntplib.MESSAGE_DELIMITER, callback)
|
|
108
|
-
|
|
109
|
-
def close(self):
|
|
110
|
-
"""Close the stream."""
|
|
111
|
-
self.stream.close()
|
|
112
|
-
self.stream = None
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
class AsyncGNTPClient(gntplib.GNTPClient):
|
|
116
|
-
"""Asynchronous GNTP client built on Tornado.
|
|
117
|
-
|
|
118
|
-
:param io_loop: :class:`tornado.ioloop.IOLoop` instance.
|
|
119
|
-
"""
|
|
120
|
-
|
|
121
|
-
def __init__(self, io_loop=None, **kwargs):
|
|
122
|
-
gntplib.GNTPClient.__init__(self, connection_class=AsyncGNTPConnection,
|
|
123
|
-
**kwargs)
|
|
124
|
-
self.io_loop = io_loop
|
|
125
|
-
|
|
126
|
-
@gen.engine
|
|
127
|
-
def process_request(self, request, callback, **kwargs):
|
|
128
|
-
"""Process a request.
|
|
129
|
-
|
|
130
|
-
If the request contains :class:`AsyncResource` instances, the
|
|
131
|
-
connection to the GNTP server will be established after all their
|
|
132
|
-
fetches are completed asynchronously.
|
|
133
|
-
"""
|
|
134
|
-
async_resources = collect_async_resources(request)
|
|
135
|
-
if async_resources:
|
|
136
|
-
yield gen.Task(fetch_async_resources_in_parallel,
|
|
137
|
-
async_resources,
|
|
138
|
-
self.io_loop)
|
|
139
|
-
gntplib.GNTPClient.process_request(self, request, callback,
|
|
140
|
-
io_loop=self.io_loop, **kwargs)
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
class AsyncResource(gntplib.Resource):
|
|
144
|
-
"""Class for asynchronous resource.
|
|
145
|
-
|
|
146
|
-
:param url: url string of the resource.
|
|
147
|
-
"""
|
|
148
|
-
def __init__(self, url):
|
|
149
|
-
gntplib.Resource.__init__(self, None)
|
|
150
|
-
self.url = url
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
class AsyncIcon(AsyncResource):
|
|
154
|
-
"""Deprecated asynchronous icon class."""
|
|
155
|
-
|
|
156
|
-
def __init__(self, url):
|
|
157
|
-
import warnings
|
|
158
|
-
warnings.warn('AsyncIcon is deprecated, use AsyncResource instead',
|
|
159
|
-
DeprecationWarning, stacklevel=2)
|
|
160
|
-
AsyncResource.__init__(self, url)
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
@gen.engine
|
|
164
|
-
def fetch_async_resources_in_parallel(async_resources, io_loop, callback):
|
|
165
|
-
"""Fetch :class:`AsyncResource`'s url in parallel."""
|
|
166
|
-
http_client = httpclient.AsyncHTTPClient(io_loop=io_loop)
|
|
167
|
-
responses = yield [gen.Task(http_client.fetch, resource.url)
|
|
168
|
-
for resource in async_resources]
|
|
169
|
-
for resource, response in zip(async_resources, responses):
|
|
170
|
-
if response.error:
|
|
171
|
-
logger.warning('failed to fetch %r: %s', resource.url,
|
|
172
|
-
response.error)
|
|
173
|
-
resource.data = None
|
|
174
|
-
else:
|
|
175
|
-
resource.data = response.body
|
|
176
|
-
callback(async_resources)
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
def collect_async_resources(request):
|
|
180
|
-
"""Collect :class:`AsyncResource` instances from given request."""
|
|
181
|
-
resources = []
|
|
182
|
-
|
|
183
|
-
if isinstance(request, gntplib.RegisterRequest):
|
|
184
|
-
resources = [request.app_icon] + [e.icon for e in request.events]
|
|
185
|
-
elif isinstance(request, gntplib.NotifyRequest):
|
|
186
|
-
resources = [request.notification.icon]
|
|
187
|
-
|
|
188
|
-
resources.extend([value for _, value in request.custom_headers
|
|
189
|
-
if isinstance(value, AsyncResource)])
|
|
190
|
-
resources.extend([value for _, value in request.app_specific_headers
|
|
191
|
-
if isinstance(value, AsyncResource)])
|
|
192
|
-
|
|
193
|
-
return list(set(r for r in resources if isinstance(r, AsyncResource)))
|
|
1
|
+
"""This module provides support for asynchronous processing built on Tornado_.
|
|
2
|
+
|
|
3
|
+
.. _Tornado: http://www.tornadoweb.org/
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import unicode_literals
|
|
7
|
+
import logging
|
|
8
|
+
import socket
|
|
9
|
+
|
|
10
|
+
from tornado import gen, httpclient, ioloop, iostream
|
|
11
|
+
|
|
12
|
+
import gntplib
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
__all__ = ['AsyncIcon', 'AsyncNotifier', 'AsyncPublisher', 'AsyncResource',
|
|
16
|
+
'AsyncSubscriber']
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
logger = logging.getLogger(__name__)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class AsyncPublisher(gntplib.Publisher):
|
|
23
|
+
"""Asynchronous Publisher of Growl Notification Transport Protocol (GNTP).
|
|
24
|
+
|
|
25
|
+
Same as :class:`~gntplib.Publisher` except the following:
|
|
26
|
+
|
|
27
|
+
* uses :class:`AsyncGNTPClient` as `gntp_client_class`.
|
|
28
|
+
* accepts :class:`tornado.ioloop.IOLoop` instance by additional
|
|
29
|
+
`io_loop` keyword argument.
|
|
30
|
+
|
|
31
|
+
:param name: the name of the application.
|
|
32
|
+
:param event_defs: the definitions of the notifications.
|
|
33
|
+
:param icon: url string or an instance of :class:`Resource` for the icon of
|
|
34
|
+
the application. Defaults to `None`.
|
|
35
|
+
:param io_loop: :class:`tornado.ioloop.IOLoop` instance.
|
|
36
|
+
:param custom_headers: the list of key-value tuples for Custom Headers.
|
|
37
|
+
:param app_specific_headers: the list of key-value tuples for App-Specific
|
|
38
|
+
Headers.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
def __init__(self, name, event_defs, icon=None, io_loop=None, **kwargs):
|
|
42
|
+
io_loop = io_loop or ioloop.IOLoop.instance()
|
|
43
|
+
gntplib.Publisher.__init__(self, name, event_defs, icon,
|
|
44
|
+
gntp_client_class=AsyncGNTPClient,
|
|
45
|
+
io_loop=io_loop, **kwargs)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class AsyncNotifier(AsyncPublisher, gntplib.Notifier):
|
|
49
|
+
"""Deprecated Asynchronous Notifier of Growl Notification Transport
|
|
50
|
+
Protocol (GNTP)."""
|
|
51
|
+
|
|
52
|
+
def __init__(self, name, event_defs, icon=None, io_loop=None, **kwargs):
|
|
53
|
+
import warnings
|
|
54
|
+
warnings.warn('AsyncNotifier is deprecated, use AsyncPublisher'
|
|
55
|
+
' instead', DeprecationWarning, stacklevel=2)
|
|
56
|
+
AsyncPublisher.__init__(self, name, event_defs, icon, io_loop,
|
|
57
|
+
**kwargs)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class AsyncSubscriber(gntplib.Subscriber):
|
|
61
|
+
"""Asynchronous Subscriber of Growl Notification Transport Protocol (GNTP).
|
|
62
|
+
|
|
63
|
+
Same as :class:`~gntplib.Subscriber` except the following:
|
|
64
|
+
|
|
65
|
+
* uses :class:`AsyncGNTPClient` as `gntp_client_class`.
|
|
66
|
+
* accepts :class:`tornado.ioloop.IOLoop` instance by additional
|
|
67
|
+
`io_loop` keyword argument.
|
|
68
|
+
|
|
69
|
+
:param name: the name of the subscriber.
|
|
70
|
+
:param hub: the subscribed-to machine. If a string is given, it is used as
|
|
71
|
+
a host of the hub and default port number `23053` is used.
|
|
72
|
+
If host-port tuple is given, it is used directly.
|
|
73
|
+
:param password: the password of the subscribed-to machine.
|
|
74
|
+
:param id_: the unique id of the subscriber. If not set, randomly
|
|
75
|
+
generated UUID is used.
|
|
76
|
+
:param port: the port number of the subscriber. Defaults to `23053`.
|
|
77
|
+
:param io_loop: :class:`tornado.ioloop.IOLoop` instance.
|
|
78
|
+
:param custom_headers: the list of key-value tuples for Custom Headers.
|
|
79
|
+
:param app_specific_headers: the list of key-value tuples for App-Specific
|
|
80
|
+
Headers.
|
|
81
|
+
"""
|
|
82
|
+
|
|
83
|
+
def __init__(self, id_, name, hub, password, port=gntplib.DEFAULT_PORT,
|
|
84
|
+
io_loop=None, **kwargs):
|
|
85
|
+
io_loop = io_loop or ioloop.IOLoop.instance()
|
|
86
|
+
gntplib.Subscriber.__init__(self, id_, name, hub, password, port=port,
|
|
87
|
+
gntp_client_class=AsyncGNTPClient,
|
|
88
|
+
io_loop=io_loop, **kwargs)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class AsyncGNTPConnection(gntplib.BaseGNTPConnection):
|
|
92
|
+
"""Asynchronous GNTP connection built on Tornado."""
|
|
93
|
+
|
|
94
|
+
def __init__(self, address, timeout, final_callback, socket_callback=None,
|
|
95
|
+
io_loop=None):
|
|
96
|
+
gntplib.BaseGNTPConnection.__init__(self, final_callback,
|
|
97
|
+
socket_callback)
|
|
98
|
+
sock = socket.create_connection(address, timeout=timeout)
|
|
99
|
+
self.stream = iostream.IOStream(sock, io_loop=io_loop)
|
|
100
|
+
|
|
101
|
+
def write_message(self, message):
|
|
102
|
+
"""Send the request to the GNTP server."""
|
|
103
|
+
self.stream.write(message)
|
|
104
|
+
|
|
105
|
+
def read_message(self, callback):
|
|
106
|
+
"""Read a message from opened stream and run callback with it."""
|
|
107
|
+
self.stream.read_until(gntplib.MESSAGE_DELIMITER, callback)
|
|
108
|
+
|
|
109
|
+
def close(self):
|
|
110
|
+
"""Close the stream."""
|
|
111
|
+
self.stream.close()
|
|
112
|
+
self.stream = None
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class AsyncGNTPClient(gntplib.GNTPClient):
|
|
116
|
+
"""Asynchronous GNTP client built on Tornado.
|
|
117
|
+
|
|
118
|
+
:param io_loop: :class:`tornado.ioloop.IOLoop` instance.
|
|
119
|
+
"""
|
|
120
|
+
|
|
121
|
+
def __init__(self, io_loop=None, **kwargs):
|
|
122
|
+
gntplib.GNTPClient.__init__(self, connection_class=AsyncGNTPConnection,
|
|
123
|
+
**kwargs)
|
|
124
|
+
self.io_loop = io_loop
|
|
125
|
+
|
|
126
|
+
@gen.engine
|
|
127
|
+
def process_request(self, request, callback, **kwargs):
|
|
128
|
+
"""Process a request.
|
|
129
|
+
|
|
130
|
+
If the request contains :class:`AsyncResource` instances, the
|
|
131
|
+
connection to the GNTP server will be established after all their
|
|
132
|
+
fetches are completed asynchronously.
|
|
133
|
+
"""
|
|
134
|
+
async_resources = collect_async_resources(request)
|
|
135
|
+
if async_resources:
|
|
136
|
+
yield gen.Task(fetch_async_resources_in_parallel,
|
|
137
|
+
async_resources,
|
|
138
|
+
self.io_loop)
|
|
139
|
+
gntplib.GNTPClient.process_request(self, request, callback,
|
|
140
|
+
io_loop=self.io_loop, **kwargs)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
class AsyncResource(gntplib.Resource):
|
|
144
|
+
"""Class for asynchronous resource.
|
|
145
|
+
|
|
146
|
+
:param url: url string of the resource.
|
|
147
|
+
"""
|
|
148
|
+
def __init__(self, url):
|
|
149
|
+
gntplib.Resource.__init__(self, None)
|
|
150
|
+
self.url = url
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
class AsyncIcon(AsyncResource):
|
|
154
|
+
"""Deprecated asynchronous icon class."""
|
|
155
|
+
|
|
156
|
+
def __init__(self, url):
|
|
157
|
+
import warnings
|
|
158
|
+
warnings.warn('AsyncIcon is deprecated, use AsyncResource instead',
|
|
159
|
+
DeprecationWarning, stacklevel=2)
|
|
160
|
+
AsyncResource.__init__(self, url)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
@gen.engine
|
|
164
|
+
def fetch_async_resources_in_parallel(async_resources, io_loop, callback):
|
|
165
|
+
"""Fetch :class:`AsyncResource`'s url in parallel."""
|
|
166
|
+
http_client = httpclient.AsyncHTTPClient(io_loop=io_loop)
|
|
167
|
+
responses = yield [gen.Task(http_client.fetch, resource.url)
|
|
168
|
+
for resource in async_resources]
|
|
169
|
+
for resource, response in zip(async_resources, responses):
|
|
170
|
+
if response.error:
|
|
171
|
+
logger.warning('failed to fetch %r: %s', resource.url,
|
|
172
|
+
response.error)
|
|
173
|
+
resource.data = None
|
|
174
|
+
else:
|
|
175
|
+
resource.data = response.body
|
|
176
|
+
callback(async_resources)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def collect_async_resources(request):
|
|
180
|
+
"""Collect :class:`AsyncResource` instances from given request."""
|
|
181
|
+
resources = []
|
|
182
|
+
|
|
183
|
+
if isinstance(request, gntplib.RegisterRequest):
|
|
184
|
+
resources = [request.app_icon] + [e.icon for e in request.events]
|
|
185
|
+
elif isinstance(request, gntplib.NotifyRequest):
|
|
186
|
+
resources = [request.notification.icon]
|
|
187
|
+
|
|
188
|
+
resources.extend([value for _, value in request.custom_headers
|
|
189
|
+
if isinstance(value, AsyncResource)])
|
|
190
|
+
resources.extend([value for _, value in request.app_specific_headers
|
|
191
|
+
if isinstance(value, AsyncResource)])
|
|
192
|
+
|
|
193
|
+
return list(set(r for r in resources if isinstance(r, AsyncResource)))
|
gntplib/ciphers.py
CHANGED
|
@@ -1,97 +1,97 @@
|
|
|
1
|
-
"""This module provides the encryption of messages built on PyCrypto_.
|
|
2
|
-
|
|
3
|
-
.. _PyCrypto: http://pycrypto.org/
|
|
4
|
-
"""
|
|
5
|
-
|
|
6
|
-
from __future__ import unicode_literals
|
|
7
|
-
import binascii
|
|
8
|
-
import struct
|
|
9
|
-
|
|
10
|
-
import Crypto.Cipher.AES
|
|
11
|
-
import Crypto.Cipher.DES
|
|
12
|
-
import Crypto.Cipher.DES3
|
|
13
|
-
|
|
14
|
-
from .utils import random_bytes
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
__all__ = ['AES', 'DES', 'DES3']
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
class _Algorithm(object):
|
|
21
|
-
"""The factory class of ciphers."""
|
|
22
|
-
|
|
23
|
-
def __init__(self, algorithm_id, key_size):
|
|
24
|
-
self.algorithm_id = algorithm_id
|
|
25
|
-
self.key_size = key_size
|
|
26
|
-
|
|
27
|
-
def cipher(self, key):
|
|
28
|
-
"""Return an instance of :class:`Cipher`."""
|
|
29
|
-
return Cipher(key, self.algorithm_id)
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
#: key length: 24 bytes (192 bit), block size: 16 byte (128 bit), iv size: 16
|
|
33
|
-
#: byte (128 bit)
|
|
34
|
-
AES = _Algorithm('AES', 24)
|
|
35
|
-
#: key length: 8 bytes (64 bit), block size: 8 byte (64 bit), iv size: 8 byte
|
|
36
|
-
#: (64 bit)
|
|
37
|
-
DES = _Algorithm('DES', 8)
|
|
38
|
-
#: key length: 24 bytes (192 bit), block size: 8 byte (64 bit), iv size: 8 byte
|
|
39
|
-
#: (64 bit)
|
|
40
|
-
DES3 = _Algorithm('3DES', 24)
|
|
41
|
-
|
|
42
|
-
ALGORITHM_MAP = {
|
|
43
|
-
'AES': Crypto.Cipher.AES,
|
|
44
|
-
'DES': Crypto.Cipher.DES,
|
|
45
|
-
'3DES': Crypto.Cipher.DES3,
|
|
46
|
-
}
|
|
47
|
-
KEY_SIZE_MAP = {
|
|
48
|
-
'AES': 24,
|
|
49
|
-
'DES': 8,
|
|
50
|
-
'3DES': 24,
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
class Cipher(object):
|
|
55
|
-
"""The encryption of messages.
|
|
56
|
-
|
|
57
|
-
:param key: the key of the encryption.
|
|
58
|
-
:param algorithm_id: the algorithm of the encryption. Specify `'AES'`,
|
|
59
|
-
`'DES'` or `'3DES'`.
|
|
60
|
-
:param iv: the initial value.
|
|
61
|
-
"""
|
|
62
|
-
|
|
63
|
-
def __init__(self, key, algorithm_id='DES', iv=None):
|
|
64
|
-
self.key = key
|
|
65
|
-
self.key_size = KEY_SIZE_MAP[algorithm_id]
|
|
66
|
-
self.algorithm = ALGORITHM_MAP[algorithm_id]
|
|
67
|
-
self.algorithm_id = algorithm_id
|
|
68
|
-
self.iv = iv or self._random_iv()
|
|
69
|
-
|
|
70
|
-
@property
|
|
71
|
-
def iv_hex(self):
|
|
72
|
-
"""The hex-encoded iv."""
|
|
73
|
-
return binascii.hexlify(self.iv)
|
|
74
|
-
|
|
75
|
-
def encrypt(self, text):
|
|
76
|
-
return self._cipher().encrypt(self._padding(text))
|
|
77
|
-
|
|
78
|
-
def decrypt(self, text):
|
|
79
|
-
return self._unpadding(self._cipher().decrypt(text))
|
|
80
|
-
|
|
81
|
-
def _cipher(self):
|
|
82
|
-
return self.algorithm.new(self.key.key[:self.key_size],
|
|
83
|
-
self.algorithm.MODE_CBC,
|
|
84
|
-
self.iv)
|
|
85
|
-
|
|
86
|
-
def _padding(self, text):
|
|
87
|
-
"""PKCS5 (PKCS7) padding."""
|
|
88
|
-
num = self.algorithm.block_size - len(text) % self.algorithm.block_size
|
|
89
|
-
return text + struct.pack(b'B' * num, *([num] * num))
|
|
90
|
-
|
|
91
|
-
def _unpadding(self, text):
|
|
92
|
-
"""Reverse PKCS5 (PKCS7) padding."""
|
|
93
|
-
num = ord(text[-1:])
|
|
94
|
-
return text[:-num]
|
|
95
|
-
|
|
96
|
-
def _random_iv(self):
|
|
97
|
-
return random_bytes(self.algorithm.block_size)
|
|
1
|
+
"""This module provides the encryption of messages built on PyCrypto_.
|
|
2
|
+
|
|
3
|
+
.. _PyCrypto: http://pycrypto.org/
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import unicode_literals
|
|
7
|
+
import binascii
|
|
8
|
+
import struct
|
|
9
|
+
|
|
10
|
+
import Crypto.Cipher.AES
|
|
11
|
+
import Crypto.Cipher.DES
|
|
12
|
+
import Crypto.Cipher.DES3
|
|
13
|
+
|
|
14
|
+
from .utils import random_bytes
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
__all__ = ['AES', 'DES', 'DES3']
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class _Algorithm(object):
|
|
21
|
+
"""The factory class of ciphers."""
|
|
22
|
+
|
|
23
|
+
def __init__(self, algorithm_id, key_size):
|
|
24
|
+
self.algorithm_id = algorithm_id
|
|
25
|
+
self.key_size = key_size
|
|
26
|
+
|
|
27
|
+
def cipher(self, key):
|
|
28
|
+
"""Return an instance of :class:`Cipher`."""
|
|
29
|
+
return Cipher(key, self.algorithm_id)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
#: key length: 24 bytes (192 bit), block size: 16 byte (128 bit), iv size: 16
|
|
33
|
+
#: byte (128 bit)
|
|
34
|
+
AES = _Algorithm('AES', 24)
|
|
35
|
+
#: key length: 8 bytes (64 bit), block size: 8 byte (64 bit), iv size: 8 byte
|
|
36
|
+
#: (64 bit)
|
|
37
|
+
DES = _Algorithm('DES', 8)
|
|
38
|
+
#: key length: 24 bytes (192 bit), block size: 8 byte (64 bit), iv size: 8 byte
|
|
39
|
+
#: (64 bit)
|
|
40
|
+
DES3 = _Algorithm('3DES', 24)
|
|
41
|
+
|
|
42
|
+
ALGORITHM_MAP = {
|
|
43
|
+
'AES': Crypto.Cipher.AES,
|
|
44
|
+
'DES': Crypto.Cipher.DES,
|
|
45
|
+
'3DES': Crypto.Cipher.DES3,
|
|
46
|
+
}
|
|
47
|
+
KEY_SIZE_MAP = {
|
|
48
|
+
'AES': 24,
|
|
49
|
+
'DES': 8,
|
|
50
|
+
'3DES': 24,
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class Cipher(object):
|
|
55
|
+
"""The encryption of messages.
|
|
56
|
+
|
|
57
|
+
:param key: the key of the encryption.
|
|
58
|
+
:param algorithm_id: the algorithm of the encryption. Specify `'AES'`,
|
|
59
|
+
`'DES'` or `'3DES'`.
|
|
60
|
+
:param iv: the initial value.
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
def __init__(self, key, algorithm_id='DES', iv=None):
|
|
64
|
+
self.key = key
|
|
65
|
+
self.key_size = KEY_SIZE_MAP[algorithm_id]
|
|
66
|
+
self.algorithm = ALGORITHM_MAP[algorithm_id]
|
|
67
|
+
self.algorithm_id = algorithm_id
|
|
68
|
+
self.iv = iv or self._random_iv()
|
|
69
|
+
|
|
70
|
+
@property
|
|
71
|
+
def iv_hex(self):
|
|
72
|
+
"""The hex-encoded iv."""
|
|
73
|
+
return binascii.hexlify(self.iv)
|
|
74
|
+
|
|
75
|
+
def encrypt(self, text):
|
|
76
|
+
return self._cipher().encrypt(self._padding(text))
|
|
77
|
+
|
|
78
|
+
def decrypt(self, text):
|
|
79
|
+
return self._unpadding(self._cipher().decrypt(text))
|
|
80
|
+
|
|
81
|
+
def _cipher(self):
|
|
82
|
+
return self.algorithm.new(self.key.key[:self.key_size],
|
|
83
|
+
self.algorithm.MODE_CBC,
|
|
84
|
+
self.iv)
|
|
85
|
+
|
|
86
|
+
def _padding(self, text):
|
|
87
|
+
"""PKCS5 (PKCS7) padding."""
|
|
88
|
+
num = self.algorithm.block_size - len(text) % self.algorithm.block_size
|
|
89
|
+
return text + struct.pack(b'B' * num, *([num] * num))
|
|
90
|
+
|
|
91
|
+
def _unpadding(self, text):
|
|
92
|
+
"""Reverse PKCS5 (PKCS7) padding."""
|
|
93
|
+
num = ord(text[-1:])
|
|
94
|
+
return text[:-num]
|
|
95
|
+
|
|
96
|
+
def _random_iv(self):
|
|
97
|
+
return random_bytes(self.algorithm.block_size)
|
gntplib/compat.py
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
"""This module makes gntplib compatible with Python 2 and 3."""
|
|
2
|
-
|
|
3
|
-
import sys
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
if sys.version_info[0] == 3:
|
|
7
|
-
text_type = str
|
|
8
|
-
else:
|
|
9
|
-
text_type = unicode
|
|
1
|
+
"""This module makes gntplib compatible with Python 2 and 3."""
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
if sys.version_info[0] == 3:
|
|
7
|
+
text_type = str
|
|
8
|
+
else:
|
|
9
|
+
text_type = unicode
|
gntplib/exceptions.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
"""This module provides a number of exceptions."""
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
class GNTPError(Exception):
|
|
5
|
-
"""Base class for GNTP exception."""
|
|
1
|
+
"""This module provides a number of exceptions."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class GNTPError(Exception):
|
|
5
|
+
"""Base class for GNTP exception."""
|