| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383 |
- # -*- coding: utf-8 -*-
- #
- # Copyright 2011 Sybren A. Stüvel <sybren@stuvel.eu>
- #
- # 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
- #
- # https://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.
- """Commandline scripts.
- These scripts are called by the executables defined in setup.py.
- """
- from __future__ import with_statement, print_function
- import abc
- import sys
- from optparse import OptionParser
- import rsa
- import rsa.bigfile
- import rsa.pkcs1
- HASH_METHODS = sorted(rsa.pkcs1.HASH_METHODS.keys())
- def keygen():
- """Key generator."""
- # Parse the CLI options
- parser = OptionParser(usage='usage: %prog [options] keysize',
- description='Generates a new RSA keypair of "keysize" bits.')
- parser.add_option('--pubout', type='string',
- help='Output filename for the public key. The public key is '
- 'not saved if this option is not present. You can use '
- 'pyrsa-priv2pub to create the public key file later.')
- parser.add_option('-o', '--out', type='string',
- help='Output filename for the private key. The key is '
- 'written to stdout if this option is not present.')
- parser.add_option('--form',
- help='key format of the private and public keys - default PEM',
- choices=('PEM', 'DER'), default='PEM')
- (cli, cli_args) = parser.parse_args(sys.argv[1:])
- if len(cli_args) != 1:
- parser.print_help()
- raise SystemExit(1)
- try:
- keysize = int(cli_args[0])
- except ValueError:
- parser.print_help()
- print('Not a valid number: %s' % cli_args[0], file=sys.stderr)
- raise SystemExit(1)
- print('Generating %i-bit key' % keysize, file=sys.stderr)
- (pub_key, priv_key) = rsa.newkeys(keysize)
- # Save public key
- if cli.pubout:
- print('Writing public key to %s' % cli.pubout, file=sys.stderr)
- data = pub_key.save_pkcs1(format=cli.form)
- with open(cli.pubout, 'wb') as outfile:
- outfile.write(data)
- # Save private key
- data = priv_key.save_pkcs1(format=cli.form)
- if cli.out:
- print('Writing private key to %s' % cli.out, file=sys.stderr)
- with open(cli.out, 'wb') as outfile:
- outfile.write(data)
- else:
- print('Writing private key to stdout', file=sys.stderr)
- sys.stdout.write(data)
- class CryptoOperation(object):
- """CLI callable that operates with input, output, and a key."""
- __metaclass__ = abc.ABCMeta
- keyname = 'public' # or 'private'
- usage = 'usage: %%prog [options] %(keyname)s_key'
- description = None
- operation = 'decrypt'
- operation_past = 'decrypted'
- operation_progressive = 'decrypting'
- input_help = 'Name of the file to %(operation)s. Reads from stdin if ' \
- 'not specified.'
- output_help = 'Name of the file to write the %(operation_past)s file ' \
- 'to. Written to stdout if this option is not present.'
- expected_cli_args = 1
- has_output = True
- key_class = rsa.PublicKey
- def __init__(self):
- self.usage = self.usage % self.__class__.__dict__
- self.input_help = self.input_help % self.__class__.__dict__
- self.output_help = self.output_help % self.__class__.__dict__
- @abc.abstractmethod
- def perform_operation(self, indata, key, cli_args=None):
- """Performs the program's operation.
- Implement in a subclass.
- :returns: the data to write to the output.
- """
- def __call__(self):
- """Runs the program."""
- (cli, cli_args) = self.parse_cli()
- key = self.read_key(cli_args[0], cli.keyform)
- indata = self.read_infile(cli.input)
- print(self.operation_progressive.title(), file=sys.stderr)
- outdata = self.perform_operation(indata, key, cli_args)
- if self.has_output:
- self.write_outfile(outdata, cli.output)
- def parse_cli(self):
- """Parse the CLI options
- :returns: (cli_opts, cli_args)
- """
- parser = OptionParser(usage=self.usage, description=self.description)
- parser.add_option('-i', '--input', type='string', help=self.input_help)
- if self.has_output:
- parser.add_option('-o', '--output', type='string', help=self.output_help)
- parser.add_option('--keyform',
- help='Key format of the %s key - default PEM' % self.keyname,
- choices=('PEM', 'DER'), default='PEM')
- (cli, cli_args) = parser.parse_args(sys.argv[1:])
- if len(cli_args) != self.expected_cli_args:
- parser.print_help()
- raise SystemExit(1)
- return cli, cli_args
- def read_key(self, filename, keyform):
- """Reads a public or private key."""
- print('Reading %s key from %s' % (self.keyname, filename), file=sys.stderr)
- with open(filename, 'rb') as keyfile:
- keydata = keyfile.read()
- return self.key_class.load_pkcs1(keydata, keyform)
- def read_infile(self, inname):
- """Read the input file"""
- if inname:
- print('Reading input from %s' % inname, file=sys.stderr)
- with open(inname, 'rb') as infile:
- return infile.read()
- print('Reading input from stdin', file=sys.stderr)
- return sys.stdin.read()
- def write_outfile(self, outdata, outname):
- """Write the output file"""
- if outname:
- print('Writing output to %s' % outname, file=sys.stderr)
- with open(outname, 'wb') as outfile:
- outfile.write(outdata)
- else:
- print('Writing output to stdout', file=sys.stderr)
- sys.stdout.write(outdata)
- class EncryptOperation(CryptoOperation):
- """Encrypts a file."""
- keyname = 'public'
- description = ('Encrypts a file. The file must be shorter than the key '
- 'length in order to be encrypted. For larger files, use the '
- 'pyrsa-encrypt-bigfile command.')
- operation = 'encrypt'
- operation_past = 'encrypted'
- operation_progressive = 'encrypting'
- def perform_operation(self, indata, pub_key, cli_args=None):
- """Encrypts files."""
- return rsa.encrypt(indata, pub_key)
- class DecryptOperation(CryptoOperation):
- """Decrypts a file."""
- keyname = 'private'
- description = ('Decrypts a file. The original file must be shorter than '
- 'the key length in order to have been encrypted. For larger '
- 'files, use the pyrsa-decrypt-bigfile command.')
- operation = 'decrypt'
- operation_past = 'decrypted'
- operation_progressive = 'decrypting'
- key_class = rsa.PrivateKey
- def perform_operation(self, indata, priv_key, cli_args=None):
- """Decrypts files."""
- return rsa.decrypt(indata, priv_key)
- class SignOperation(CryptoOperation):
- """Signs a file."""
- keyname = 'private'
- usage = 'usage: %%prog [options] private_key hash_method'
- description = ('Signs a file, outputs the signature. Choose the hash '
- 'method from %s' % ', '.join(HASH_METHODS))
- operation = 'sign'
- operation_past = 'signature'
- operation_progressive = 'Signing'
- key_class = rsa.PrivateKey
- expected_cli_args = 2
- output_help = ('Name of the file to write the signature to. Written '
- 'to stdout if this option is not present.')
- def perform_operation(self, indata, priv_key, cli_args):
- """Signs files."""
- hash_method = cli_args[1]
- if hash_method not in HASH_METHODS:
- raise SystemExit('Invalid hash method, choose one of %s' %
- ', '.join(HASH_METHODS))
- return rsa.sign(indata, priv_key, hash_method)
- class VerifyOperation(CryptoOperation):
- """Verify a signature."""
- keyname = 'public'
- usage = 'usage: %%prog [options] public_key signature_file'
- description = ('Verifies a signature, exits with status 0 upon success, '
- 'prints an error message and exits with status 1 upon error.')
- operation = 'verify'
- operation_past = 'verified'
- operation_progressive = 'Verifying'
- key_class = rsa.PublicKey
- expected_cli_args = 2
- has_output = False
- def perform_operation(self, indata, pub_key, cli_args):
- """Verifies files."""
- signature_file = cli_args[1]
- with open(signature_file, 'rb') as sigfile:
- signature = sigfile.read()
- try:
- rsa.verify(indata, signature, pub_key)
- except rsa.VerificationError:
- raise SystemExit('Verification failed.')
- print('Verification OK', file=sys.stderr)
- class BigfileOperation(CryptoOperation):
- """CryptoOperation that doesn't read the entire file into memory."""
- def __init__(self):
- CryptoOperation.__init__(self)
- self.file_objects = []
- def __del__(self):
- """Closes any open file handles."""
- for fobj in self.file_objects:
- fobj.close()
- def __call__(self):
- """Runs the program."""
- (cli, cli_args) = self.parse_cli()
- key = self.read_key(cli_args[0], cli.keyform)
- # Get the file handles
- infile = self.get_infile(cli.input)
- outfile = self.get_outfile(cli.output)
- # Call the operation
- print(self.operation_progressive.title(), file=sys.stderr)
- self.perform_operation(infile, outfile, key, cli_args)
- def get_infile(self, inname):
- """Returns the input file object"""
- if inname:
- print('Reading input from %s' % inname, file=sys.stderr)
- fobj = open(inname, 'rb')
- self.file_objects.append(fobj)
- else:
- print('Reading input from stdin', file=sys.stderr)
- fobj = sys.stdin
- return fobj
- def get_outfile(self, outname):
- """Returns the output file object"""
- if outname:
- print('Will write output to %s' % outname, file=sys.stderr)
- fobj = open(outname, 'wb')
- self.file_objects.append(fobj)
- else:
- print('Will write output to stdout', file=sys.stderr)
- fobj = sys.stdout
- return fobj
- class EncryptBigfileOperation(BigfileOperation):
- """Encrypts a file to VARBLOCK format."""
- keyname = 'public'
- description = ('Encrypts a file to an encrypted VARBLOCK file. The file '
- 'can be larger than the key length, but the output file is only '
- 'compatible with Python-RSA.')
- operation = 'encrypt'
- operation_past = 'encrypted'
- operation_progressive = 'encrypting'
- def perform_operation(self, infile, outfile, pub_key, cli_args=None):
- """Encrypts files to VARBLOCK."""
- return rsa.bigfile.encrypt_bigfile(infile, outfile, pub_key)
- class DecryptBigfileOperation(BigfileOperation):
- """Decrypts a file in VARBLOCK format."""
- keyname = 'private'
- description = ('Decrypts an encrypted VARBLOCK file that was encrypted '
- 'with pyrsa-encrypt-bigfile')
- operation = 'decrypt'
- operation_past = 'decrypted'
- operation_progressive = 'decrypting'
- key_class = rsa.PrivateKey
- def perform_operation(self, infile, outfile, priv_key, cli_args=None):
- """Decrypts a VARBLOCK file."""
- return rsa.bigfile.decrypt_bigfile(infile, outfile, priv_key)
- encrypt = EncryptOperation()
- decrypt = DecryptOperation()
- sign = SignOperation()
- verify = VerifyOperation()
- encrypt_bigfile = EncryptBigfileOperation()
- decrypt_bigfile = DecryptBigfileOperation()
|