]> gitweb.pimeys.fr Git - NK2015_Client_Python_Alpha.git/blob - rsa_source/rsa/cli.py
on ajoute le module rsa car le client aussi en a besoin
[NK2015_Client_Python_Alpha.git] / rsa_source / rsa / cli.py
1 # -*- coding: utf-8 -*-
2 #
3 # Copyright 2011 Sybren A. Stüvel <sybren@stuvel.eu>
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 '''Commandline scripts.
18
19 These scripts are called by the executables defined in setup.py.
20 '''
21
22 import abc
23 import sys
24 from optparse import OptionParser
25
26 import rsa
27 import rsa.bigfile
28 import rsa.pkcs1
29
30 HASH_METHODS = sorted(rsa.pkcs1.HASH_METHODS.keys())
31
32 def keygen():
33 '''Key generator.'''
34
35 # Parse the CLI options
36 parser = OptionParser(usage='usage: %prog [options] keysize',
37 description='Generates a new RSA keypair of "keysize" bits.')
38
39 parser.add_option('--pubout', type='string',
40 help='Output filename for the public key. The public key is '
41 'not saved if this option is not present. You can use '
42 'pyrsa-priv2pub to create the public key file later.')
43
44 parser.add_option('-o', '--out', type='string',
45 help='Output filename for the private key. The key is '
46 'written to stdout if this option is not present.')
47
48 parser.add_option('--form',
49 help='key format of the private and public keys - default PEM',
50 choices=('PEM', 'DER'), default='PEM')
51
52 (cli, cli_args) = parser.parse_args(sys.argv[1:])
53
54 if len(cli_args) != 1:
55 parser.print_help()
56 raise SystemExit(1)
57
58 try:
59 keysize = int(cli_args[0])
60 except ValueError:
61 parser.print_help()
62 print >>sys.stderr, 'Not a valid number: %s' % cli_args[0]
63 raise SystemExit(1)
64
65 print >>sys.stderr, 'Generating %i-bit key' % keysize
66 (pub_key, priv_key) = rsa.newkeys(keysize)
67
68
69 # Save public key
70 if cli.pubout:
71 print >>sys.stderr, 'Writing public key to %s' % cli.pubout
72 data = pub_key.save_pkcs1(format=cli.form)
73 with open(cli.pubout, 'w') as outfile:
74 outfile.write(data)
75
76 # Save private key
77 data = priv_key.save_pkcs1(format=cli.form)
78
79 if cli.out:
80 print >>sys.stderr, 'Writing private key to %s' % cli.out
81 with open(cli.out, 'w') as outfile:
82 outfile.write(data)
83 else:
84 print >>sys.stderr, 'Writing private key to stdout'
85 sys.stdout.write(data)
86
87
88 class CryptoOperation(object):
89 '''CLI callable that operates with input, output, and a key.'''
90
91 __metaclass__ = abc.ABCMeta
92
93 keyname = 'public' # or 'private'
94 usage = 'usage: %%prog [options] %(keyname)s_key'
95 description = None
96 operation = 'decrypt'
97 operation_past = 'decrypted'
98 operation_progressive = 'decrypting'
99 input_help = 'Name of the file to %(operation)s. Reads from stdin if ' \
100 'not specified.'
101 output_help = 'Name of the file to write the %(operation_past)s file ' \
102 'to. Written to stdout if this option is not present.'
103 expected_cli_args = 1
104 has_output = True
105
106 key_class = rsa.PublicKey
107
108 def __init__(self):
109 self.usage = self.usage % self.__class__.__dict__
110 self.input_help = self.input_help % self.__class__.__dict__
111 self.output_help = self.output_help % self.__class__.__dict__
112
113 @abc.abstractmethod
114 def perform_operation(self, indata, key, cli_args=None):
115 '''Performs the program's operation.
116
117 Implement in a subclass.
118
119 :returns: the data to write to the output.
120 '''
121
122 def __call__(self):
123 '''Runs the program.'''
124
125 (cli, cli_args) = self.parse_cli()
126
127 key = self.read_key(cli_args[0], cli.keyform)
128
129 indata = self.read_infile(cli.input)
130
131 print >>sys.stderr, self.operation_progressive.title()
132 outdata = self.perform_operation(indata, key, cli_args)
133
134 if self.has_output:
135 self.write_outfile(outdata, cli.output)
136
137 def parse_cli(self):
138 '''Parse the CLI options
139
140 :returns: (cli_opts, cli_args)
141 '''
142
143 parser = OptionParser(usage=self.usage, description=self.description)
144
145 parser.add_option('-i', '--input', type='string', help=self.input_help)
146
147 if self.has_output:
148 parser.add_option('-o', '--output', type='string', help=self.output_help)
149
150 parser.add_option('--keyform',
151 help='Key format of the %s key - default PEM' % self.keyname,
152 choices=('PEM', 'DER'), default='PEM')
153
154 (cli, cli_args) = parser.parse_args(sys.argv[1:])
155
156 if len(cli_args) != self.expected_cli_args:
157 parser.print_help()
158 raise SystemExit(1)
159
160 return (cli, cli_args)
161
162 def read_key(self, filename, keyform):
163 '''Reads a public or private key.'''
164
165 print >>sys.stderr, 'Reading %s key from %s' % (self.keyname, filename)
166 with open(filename) as keyfile:
167 keydata = keyfile.read()
168
169 return self.key_class.load_pkcs1(keydata, keyform)
170
171 def read_infile(self, inname):
172 '''Read the input file'''
173
174 if inname:
175 print >>sys.stderr, 'Reading input from %s' % inname
176 with open(inname, 'rb') as infile:
177 return infile.read()
178
179 print >>sys.stderr, 'Reading input from stdin'
180 return sys.stdin.read()
181
182 def write_outfile(self, outdata, outname):
183 '''Write the output file'''
184
185 if outname:
186 print >>sys.stderr, 'Writing output to %s' % outname
187 with open(outname, 'wb') as outfile:
188 outfile.write(outdata)
189 else:
190 print >>sys.stderr, 'Writing output to stdout'
191 sys.stdout.write(outdata)
192
193 class EncryptOperation(CryptoOperation):
194 '''Encrypts a file.'''
195
196 keyname = 'public'
197 description = ('Encrypts a file. The file must be shorter than the key '
198 'length in order to be encrypted. For larger files, use the '
199 'pyrsa-encrypt-bigfile command.')
200 operation = 'encrypt'
201 operation_past = 'encrypted'
202 operation_progressive = 'encrypting'
203
204
205 def perform_operation(self, indata, pub_key, cli_args=None):
206 '''Encrypts files.'''
207
208 return rsa.encrypt(indata, pub_key)
209
210 class DecryptOperation(CryptoOperation):
211 '''Decrypts a file.'''
212
213 keyname = 'private'
214 description = ('Decrypts a file. The original file must be shorter than '
215 'the key length in order to have been encrypted. For larger '
216 'files, use the pyrsa-decrypt-bigfile command.')
217 operation = 'decrypt'
218 operation_past = 'decrypted'
219 operation_progressive = 'decrypting'
220 key_class = rsa.PrivateKey
221
222 def perform_operation(self, indata, priv_key, cli_args=None):
223 '''Decrypts files.'''
224
225 return rsa.decrypt(indata, priv_key)
226
227 class SignOperation(CryptoOperation):
228 '''Signs a file.'''
229
230 keyname = 'private'
231 usage = 'usage: %%prog [options] private_key hash_method'
232 description = ('Signs a file, outputs the signature. Choose the hash '
233 'method from %s' % ', '.join(HASH_METHODS))
234 operation = 'sign'
235 operation_past = 'signature'
236 operation_progressive = 'Signing'
237 key_class = rsa.PrivateKey
238 expected_cli_args = 2
239
240 output_help = ('Name of the file to write the signature to. Written '
241 'to stdout if this option is not present.')
242
243 def perform_operation(self, indata, priv_key, cli_args):
244 '''Decrypts files.'''
245
246 hash_method = cli_args[1]
247 if hash_method not in HASH_METHODS:
248 raise SystemExit('Invalid hash method, choose one of %s' %
249 ', '.join(HASH_METHODS))
250
251 return rsa.sign(indata, priv_key, hash_method)
252
253 class VerifyOperation(CryptoOperation):
254 '''Verify a signature.'''
255
256 keyname = 'public'
257 usage = 'usage: %%prog [options] private_key signature_file'
258 description = ('Verifies a signature, exits with status 0 upon success, '
259 'prints an error message and exits with status 1 upon error.')
260 operation = 'verify'
261 operation_past = 'verified'
262 operation_progressive = 'Verifying'
263 key_class = rsa.PublicKey
264 expected_cli_args = 2
265 has_output = False
266
267 def perform_operation(self, indata, pub_key, cli_args):
268 '''Decrypts files.'''
269
270 signature_file = cli_args[1]
271
272 with open(signature_file, 'rb') as sigfile:
273 signature = sigfile.read()
274
275 try:
276 rsa.verify(indata, signature, pub_key)
277 except rsa.VerificationError:
278 raise SystemExit('Verification failed.')
279
280 print >>sys.stderr, 'Verification OK'
281
282
283 class BigfileOperation(CryptoOperation):
284 '''CryptoOperation that doesn't read the entire file into memory.'''
285
286 def __init__(self):
287 CryptoOperation.__init__(self)
288
289 self.file_objects = []
290
291 def __del__(self):
292 '''Closes any open file handles.'''
293
294 for fobj in self.file_objects:
295 fobj.close()
296
297 def __call__(self):
298 '''Runs the program.'''
299
300 (cli, cli_args) = self.parse_cli()
301
302 key = self.read_key(cli_args[0], cli.keyform)
303
304 # Get the file handles
305 infile = self.get_infile(cli.input)
306 outfile = self.get_outfile(cli.output)
307
308 # Call the operation
309 print >>sys.stderr, self.operation_progressive.title()
310 self.perform_operation(infile, outfile, key, cli_args)
311
312 def get_infile(self, inname):
313 '''Returns the input file object'''
314
315 if inname:
316 print >>sys.stderr, 'Reading input from %s' % inname
317 fobj = open(inname, 'rb')
318 self.file_objects.append(fobj)
319 else:
320 print >>sys.stderr, 'Reading input from stdin'
321 fobj = sys.stdin
322
323 return fobj
324
325 def get_outfile(self, outname):
326 '''Returns the output file object'''
327
328 if outname:
329 print >>sys.stderr, 'Will write output to %s' % outname
330 fobj = open(outname, 'wb')
331 self.file_objects.append(fobj)
332 else:
333 print >>sys.stderr, 'Will write output to stdout'
334 fobj = sys.stdout
335
336 return fobj
337
338 class EncryptBigfileOperation(BigfileOperation):
339 '''Encrypts a file to VARBLOCK format.'''
340
341 keyname = 'public'
342 description = ('Encrypts a file to an encrypted VARBLOCK file. The file '
343 'can be larger than the key length, but the output file is only '
344 'compatible with Python-RSA.')
345 operation = 'encrypt'
346 operation_past = 'encrypted'
347 operation_progressive = 'encrypting'
348
349 def perform_operation(self, infile, outfile, pub_key, cli_args=None):
350 '''Encrypts files to VARBLOCK.'''
351
352 return rsa.bigfile.encrypt_bigfile(infile, outfile, pub_key)
353
354 class DecryptBigfileOperation(BigfileOperation):
355 '''Decrypts a file in VARBLOCK format.'''
356
357 keyname = 'private'
358 description = ('Decrypts an encrypted VARBLOCK file that was encrypted '
359 'with pyrsa-encrypt-bigfile')
360 operation = 'decrypt'
361 operation_past = 'decrypted'
362 operation_progressive = 'decrypting'
363 key_class = rsa.PrivateKey
364
365 def perform_operation(self, infile, outfile, priv_key, cli_args=None):
366 '''Decrypts a VARBLOCK file.'''
367
368 return rsa.bigfile.decrypt_bigfile(infile, outfile, priv_key)
369
370
371 encrypt = EncryptOperation()
372 decrypt = DecryptOperation()
373 sign = SignOperation()
374 verify = VerifyOperation()
375 encrypt_bigfile = EncryptBigfileOperation()
376 decrypt_bigfile = DecryptBigfileOperation()
377