Esempio n. 1
0
def generate_public_and_private():
    """
  <Purpose> 
    Generate a pair of ed25519 public and private keys with PyNaCl.  The public
    and private keys returned conform to 'tuf.formats.ED25519PULIC_SCHEMA' and
    'tuf.formats.ED25519SEED_SCHEMA', respectively, and have the form:
    
    '\xa2F\x99\xe0\x86\x80%\xc8\xee\x11\xb95T\xd9\...'

    An ed25519 seed key is a random 32-byte string.  Public keys are also 32
    bytes.

    >>> public, private = generate_public_and_private()
    >>> tuf.formats.ED25519PUBLIC_SCHEMA.matches(public)
    True
    >>> tuf.formats.ED25519SEED_SCHEMA.matches(private)
    True

  <Arguments>
    None.

  <Exceptions>
    tuf.UnsupportedLibraryError, if the PyNaCl ('nacl') module is unavailable.

    NotImplementedError, if a randomness source is not found by 'os.urandom'.

  <Side Effects>
    The ed25519 keys are generated by first creating a random 32-byte seed
    with os.urandom() and then calling PyNaCl's nacl.signing.SigningKey().

  <Returns>
    A (public, private) tuple that conform to 'tuf.formats.ED25519PUBLIC_SCHEMA'
    and 'tuf.formats.ED25519SEED_SCHEMA', respectively.
  """

    # Generate ed25519's seed key by calling os.urandom().  The random bytes
    # returned should be suitable for cryptographic use and is OS-specific.
    # Raise 'NotImplementedError' if a randomness source is not found.
    # ed25519 seed keys are fixed at 32 bytes (256-bit keys).
    # http://blog.mozilla.org/warner/2011/11/29/ed25519-keys/
    seed = os.urandom(32)
    public = None

    # Generate the public key.  PyNaCl (i.e., 'nacl' module) performs the actual
    # key generation.
    try:
        nacl_key = nacl.signing.SigningKey(seed)
        public = nacl_key.verify_key.encode(encoder=nacl.encoding.RawEncoder())

    except NameError:  # pragma: no cover
        message = 'The PyNaCl library and/or its dependencies unavailable.'
        raise tuf.UnsupportedLibraryError(message)

    return public, seed
Esempio n. 2
0
def verify_signature(public_key, method, signature, data, use_pynacl=False):
    """
  <Purpose>
    Determine whether the private key corresponding to 'public_key' produced
    'signature'.  verify_signature() will use the public key, the 'method' and
    'sig', and 'data' arguments to complete the verification.

    >>> public, private = generate_public_and_private()
    >>> data = b'The quick brown fox jumps over the lazy dog'
    >>> signature, method = \
        create_signature(public, private, data)
    >>> verify_signature(public, method, signature, data, use_pynacl=False)
    True
    >>> verify_signature(public, method, signature, data, use_pynacl=True)
    True
    >>> bad_data = b'The sly brown fox jumps over the lazy dog'
    >>> bad_signature, method = \
        create_signature(public, private, bad_data)
    >>> verify_signature(public, method, bad_signature, data, use_pynacl=False)
    False
  
  <Arguments>
    public_key:
      The public key is a 32-byte string.

    method:
      'ed25519' signature method generated by either the pure python
      implementation (i.e., 'tuf._vendor.ed25519.ed25519.py') or PyNacl
      (i.e., 'nacl').
      
    signature:
      The signature is a 64-byte string. 
      
    data:
      Data object used by tuf.ed25519_keys.create_signature() to generate
      'signature'.  'data' is needed here to verify the signature.
    
    use_pynacl:
      True, if the ed25519 signature should be verified by PyNaCl.  False,
      if the signature should be verified with the pure Python implementation
      of ed25519 (slower).

  <Exceptions>
    tuf.UnknownMethodError.  Raised if the signing method used by
    'signature' is not one supported by tuf.ed25519_keys.create_signature().
    
    tuf.FormatError. Raised if the arguments are improperly formatted. 

  <Side Effects>
    tuf._vendor.ed25519.ed25519.checkvalid() called to do the actual
    verification.  nacl.signing.VerifyKey.verify() called if 'use_pynacl' is
    True.

  <Returns>
    Boolean.  True if the signature is valid, False otherwise.
  """

    # Does 'public_key' have the correct format?
    # This check will ensure 'public_key' conforms to
    # 'tuf.formats.ED25519PUBLIC_SCHEMA', which must have length 32 bytes.
    # Raise 'tuf.FormatError' if the check fails.
    tuf.formats.ED25519PUBLIC_SCHEMA.check_match(public_key)

    # Is 'method' properly formatted?
    tuf.formats.NAME_SCHEMA.check_match(method)

    # Is 'signature' properly formatted?
    tuf.formats.ED25519SIGNATURE_SCHEMA.check_match(signature)

    # Is 'use_pynacl' properly formatted?
    tuf.formats.BOOLEAN_SCHEMA.check_match(use_pynacl)

    # Verify 'signature'.  Before returning the Boolean result,
    # ensure 'ed25519' was used as the signing method.
    # Raise 'tuf.UnsupportedLibraryError' if 'use_pynacl' is True but 'nacl' is
    # unavailable.
    public = public_key
    valid_signature = False

    if method in _SUPPORTED_ED25519_SIGNING_METHODS:
        if use_pynacl:
            try:
                nacl_verify_key = nacl.signing.VerifyKey(public)
                nacl_message = nacl_verify_key.verify(data, signature)
                valid_signature = True

            except NameError:  # pragma: no cover
                message = 'The PyNaCl library and/or its dependencies unavailable.'
                raise tuf.UnsupportedLibraryError(message)

            except nacl.exceptions.BadSignatureError:
                pass

        # Verify 'ed25519' signature with the pure Python implementation.
        else:
            try:
                tuf._vendor.ed25519.ed25519.checkvalid(signature, data, public)
                valid_signature = True

            # The pure Python implementation raises 'Exception' if 'signature' is
            # invalid.
            except Exception as e:
                pass

    else:
        message = 'Unsupported ed25519 signing method: '+repr(method)+'.\n'+ \
          'Supported methods: '+repr(_SUPPORTED_ED25519_SIGNING_METHODS)+'.'
        raise tuf.UnknownMethodError(message)

    return valid_signature
Esempio n. 3
0
def create_signature(public_key, private_key, data):
    """
  <Purpose>
    Return a (signature, method) tuple, where the method is 'ed25519' and is
    always generated by PyNaCl (i.e., 'nacl').  The signature returned conforms
    to 'tuf.formats.ED25519SIGNATURE_SCHEMA', and has the form:
    
    '\xae\xd7\x9f\xaf\x95{bP\x9e\xa8YO Z\x86\x9d...'

    A signature is a 64-byte string.

    >>> public, private = generate_public_and_private()
    >>> data = b'The quick brown fox jumps over the lazy dog'
    >>> signature, method = \
        create_signature(public, private, data)
    >>> tuf.formats.ED25519SIGNATURE_SCHEMA.matches(signature)
    True
    >>> method == 'ed25519'
    True
    >>> signature, method = \
        create_signature(public, private, data)
    >>> tuf.formats.ED25519SIGNATURE_SCHEMA.matches(signature)
    True
    >>> method == 'ed25519'
    True

  <Arguments>
    public:
      The ed25519 public key, which is a 32-byte string.
    
    private:
      The ed25519 private key, which is a 32-byte string.

    data:
      Data object used by create_signature() to generate the signature.

  <Exceptions>
    tuf.FormatError, if the arguments are improperly formatted.

    tuf.CryptoError, if a signature cannot be created.

  <Side Effects>
    nacl.signing.SigningKey.sign() called to generate the actual signature.

  <Returns>
    A signature dictionary conformat to 'tuf.format.SIGNATURE_SCHEMA'.
    ed25519 signatures are 64 bytes, however, the hexlified signature is
    stored in the dictionary returned.
  """

    # Does 'public_key' have the correct format?
    # This check will ensure 'public_key' conforms to
    # 'tuf.formats.ED25519PUBLIC_SCHEMA', which must have length 32 bytes.
    # Raise 'tuf.FormatError' if the check fails.
    tuf.formats.ED25519PUBLIC_SCHEMA.check_match(public_key)

    # Is 'private_key' properly formatted?
    tuf.formats.ED25519SEED_SCHEMA.check_match(private_key)

    # Signing the 'data' object requires a seed and public key.
    # nacl.signing.SigningKey.sign() generates the signature.
    public = public_key
    private = private_key

    method = None
    signature = None

    # The private and public keys have been validated above by 'tuf.formats' and
    # should be 32-byte strings.
    method = 'ed25519'
    try:
        nacl_key = nacl.signing.SigningKey(private)
        nacl_sig = nacl_key.sign(data)
        signature = nacl_sig.signature

    except NameError:  # pragma: no cover
        message = 'The PyNaCl library and/or its dependencies unavailable.'
        raise tuf.UnsupportedLibraryError(message)

    except (ValueError, TypeError, nacl.exceptions.CryptoError) as e:
        message = 'An "ed25519" signature could not be created with PyNaCl.'
        raise tuf.CryptoError(message + str(e))

    return signature, method
Esempio n. 4
0
def import_rsakey_from_encrypted_pem(encrypted_pem, password):
  """
  <Purpose> 
    Import the public and private RSA keys stored in 'encrypted_pem'.  In
    addition, a keyid identifier for the RSA key is generated.  The object
    returned conforms to 'tuf.formats.RSAKEY_SCHEMA' and has the
    form:
    
    {'keytype': 'rsa',
     'keyid': keyid,
     'keyval': {'public': '-----BEGIN RSA PUBLIC KEY----- ...',
                'private': '-----BEGIN RSA PRIVATE KEY----- ...'}}
    
    The public and private keys are strings in PEM format.

    Although the PyCrypto crytography library called sets a 1024-bit minimum
    key size, generate() enforces a minimum key size of 2048 bits.  If 'bits' is
    unspecified, a 3072-bit RSA key is generated, which is the key size
    recommended by TUF. 
    
    >>> rsa_key = generate_rsa_key()
    >>> private = rsa_key['keyval']['private']
    >>> passphrase = 'secret'
    >>> encrypted_pem = create_rsa_encrypted_pem(private, passphrase) 
    >>> rsa_key2 = import_rsakey_from_encrypted_pem(encrypted_pem, passphrase)
    >>> rsa_key == rsa_key2
    True
  
  <Arguments>
    encrypted_pem:
      A string in PEM format.

    password:
      The password, or passphrase, to decrypt the private part of the RSA
      key.  'password' is not used directly as the encryption key, a stronger
      encryption key is derived from it.

  <Exceptions>
    tuf.FormatError, if the arguments are improperly formatted.
   
    tuf.UnsupportedLibraryError, if any of the cryptography libraries specified
    in 'tuf.conf.py' are unsupported or unavailable.

    ValueError, if an exception occurs after calling the RSA key generation
    routine.  'bits' must be a multiple of 256.  The 'ValueError' exception is
    raised by the key generation function of the cryptography library called.

  <Side Effects>
    The RSA keys are generated by calling PyCrypto's
    Crypto.PublicKey.RSA.generate().

  <Returns>
    A dictionary containing the RSA keys and other identifying information.
    Conforms to 'tuf.formats.RSAKEY_SCHEMA'. 
  """

  # Does 'encrypted_pem' have the correct format?
  # This check will ensure 'encrypted_pem' conforms to
  # 'tuf.formats.PEMRSA_SCHEMA'.
  tuf.formats.PEMRSA_SCHEMA.check_match(encrypted_pem)

  # Does 'password' have the correct format?
  tuf.formats.PASSWORD_SCHEMA.check_match(password)

  # Raise 'tuf.UnsupportedLibraryError' if the following libraries, specified in
  # 'tuf.conf', are unsupported or unavailable:
  # 'tuf.conf.RSA_CRYPTO_LIBRARY' and 'tuf.conf.GENERAL_CRYPTO_LIBRARY'. 
  check_crypto_libraries(['rsa', 'general'])

  # Begin building the RSA key dictionary. 
  rsakey_dict = {}
  keytype = 'rsa'
  public = None
  private = None

  # Generate the public and private RSA keys.  The PyCrypto module performs
  # the actual key generation.  Raise 'ValueError' if 'bits' is less than 1024 
  # or not a multiple of 256, although a 2048-bit minimum is enforced by
  # tuf.formats.RSAKEYBITS_SCHEMA.check_match().
  if _RSA_CRYPTO_LIBRARY == 'pycrypto':
    public, private = \
      tuf.pycrypto_keys.create_rsa_public_and_private_from_encrypted_pem(encrypted_pem,
                                                                         password)
  else: #pragma: no cover
    message = 'Invalid crypto library: ' + repr(_RSA_CRYPTO_LIBRARY) + '.'
    raise tuf.UnsupportedLibraryError(message) 
    
  # Generate the keyid of the RSA key.  'key_value' corresponds to the
  # 'keyval' entry of the 'RSAKEY_SCHEMA' dictionary.  The private key
  # information is not included in the generation of the 'keyid' identifier.
  key_value = {'public': public,
               'private': ''}
  keyid = _get_keyid(keytype, key_value)

  # Build the 'rsakey_dict' dictionary.  Update 'key_value' with the RSA
  # private key prior to adding 'key_value' to 'rsakey_dict'.
  key_value['private'] = private

  rsakey_dict['keytype'] = keytype
  rsakey_dict['keyid'] = keyid
  rsakey_dict['keyval'] = key_value

  return rsakey_dict
Esempio n. 5
0
def digest(algorithm=_DEFAULT_HASH_ALGORITHM, 
           hash_library=_DEFAULT_HASH_LIBRARY):
  """
  <Purpose>
    Provide the caller with the ability to create
    digest objects without having to worry about hash
    library availability or which library to use.  
    The caller also has the option of specifying which
    hash algorithm and/or library to use.

    # Creation of a digest object using defaults
    # or by specifying hash algorithm and library.
    digest_object = tuf.hash.digest()
    digest_object = tuf.hash.digest('sha384')
    digest_object = tuf.hash.digest('pycrypto')

    # The expected interface for digest objects. 
    digest_object.digest_size
    digest_object.hexdigest()
    digest_object.update('data')
    digest_object.digest()
    
    # Added hash routines by this module.
    digest_object = tuf.hash.digest_fileobject(file_object)
    digest_object = tuf.hash.digest_filename(filename)
  
  <Arguments>
    algorithm:
      The hash algorithm (e.g., md5, sha1, sha256).

    hash_library:
      The library providing the hash algorithms 
      (e.g., pycrypto, hashlib).
      
  <Exceptions>
    tuf.UnsupportedAlgorithmError
    tuf.UnsupportedLibraryError

  <Side Effects>
    None.

  <Returns>
    Digest object (e.g., hashlib.new(algorithm) or 
    algorithm.new() # pycrypto).

  """

  # Was a hashlib digest object requested and is it supported?
  # If so, return the digest object.
  if hash_library == 'hashlib' and hash_library in _supported_libraries:
    try:
      return hashlib.new(algorithm)
    except ValueError:
      raise tuf.UnsupportedAlgorithmError(algorithm)

  # Was a pycrypto digest object requested and is it supported?
  elif hash_library == 'pycrypto' and hash_library in _supported_libraries:
    # Pycrypto does not offer a comparable hashlib.new(hashname).
    # Let's first check the 'algorithm' argument before returning
    # the correct pycrypto digest object using pycrypto's object construction. 
    if algorithm == 'md5':
      return MD5.new()
    elif algorithm == 'sha1':
      return SHA.new()
    elif algorithm == 'sha224':
      return SHA224.new()
    elif algorithm == 'sha256':
      return SHA256.new()
    elif algorithm == 'sha384':
      return SHA384.new()
    elif algorithm == 'sha512':
      return SHA512.new()
    else:
      raise tuf.UnsupportedAlgorithmError(algorithm)
  
  # The requested hash library is not supported. 
  else:
    raise tuf.UnsupportedLibraryError('Unsupported library requested.  '
                    'Supported hash libraries: '+str(_SUPPORTED_LIB_LIST)) 
Esempio n. 6
0
def create_signature(key_dict, data):
  """
  <Purpose>
    Return a signature dictionary of the form:
    {'keyid': 'f30a0870d026980100c0573bd557394f8c1bbd6...',
     'method': '...',
     'sig': '...'}.

    The signing process will use the private key in 
    key_dict['keyval']['private'] and 'data' to generate the signature.

    The following signature methods are supported:

    'RSASSA-PSS' 
    RFC3447 - RSASSA-PSS 
    http://www.ietf.org/rfc/rfc3447.

    'ed25519'
    ed25519 - high-speed high security signatures 
    http://ed25519.cr.yp.to/

    Which signature to generate is determined by the key type of 'key_dict'
    and the available cryptography library specified in 'tuf.conf'.
    
    >>> ed25519_key = generate_ed25519_key()
    >>> data = 'The quick brown fox jumps over the lazy dog'
    >>> signature = create_signature(ed25519_key, data)
    >>> tuf.formats.SIGNATURE_SCHEMA.matches(signature)
    True
    >>> len(signature['sig'])
    128
    >>> rsa_key = generate_rsa_key(2048)
    >>> data = 'The quick brown fox jumps over the lazy dog'
    >>> signature = create_signature(rsa_key, data)
    >>> tuf.formats.SIGNATURE_SCHEMA.matches(signature)
    True

  <Arguments>
    key_dict:
      A dictionary containing the TUF keys.  An example RSA key dict has the
      form:
    
      {'keytype': 'rsa',
       'keyid': 'f30a0870d026980100c0573bd557394f8c1bbd6...',
       'keyval': {'public': '-----BEGIN RSA PUBLIC KEY----- ...',
                  'private': '-----BEGIN RSA PRIVATE KEY----- ...'}}

      The public and private keys are strings in PEM format.

    data:
      Data object used by create_signature() to generate the signature.

  <Exceptions>
    tuf.FormatError, if 'key_dict' is improperly formatted.
   
    tuf.UnsupportedLibraryError, if an unsupported or unavailable library is
    detected.

    TypeError, if 'key_dict' contains an invalid keytype.

  <Side Effects>
    The cryptography library specified in 'tuf.conf' called to perform the
    actual signing routine.

  <Returns>
    A signature dictionary conformat to 'tuf.format.SIGNATURE_SCHEMA'.
  """

  # Does 'key_dict' have the correct format?
  # This check will ensure 'key_dict' has the appropriate number of objects
  # and object types, and that all dict keys are properly named.
  # Raise 'tuf.FormatError' if the check fails.
  # The key type of 'key_dict' must be either 'rsa' or 'ed25519'.
  tuf.formats.ANYKEY_SCHEMA.check_match(key_dict)
  
  # Raise 'tuf.UnsupportedLibraryError' if the following libraries, specified
  # in 'tuf.conf', are unsupported or unavailable:
  # 'tuf.conf.RSA_CRYPTO_LIBRARY' or 'tuf.conf.ED25519_CRYPTO_LIBRARY'. 
  check_crypto_libraries([key_dict['keytype']])

  # Signing the 'data' object requires a private key.
  # The 'RSASSA-PSS' (i.e., PyCrypto module) and 'ed25519' (i.e., PyNaCl and the
  # optimized pure Python implementation of ed25519) are the only signing
  # methods  currently supported.
  signature = {}
  keytype = key_dict['keytype']
  public = key_dict['keyval']['public']
  private = key_dict['keyval']['private']
  keyid = key_dict['keyid']
  method = None
  sig = None

  # Convert 'data' to canonical JSON format so that repeatable signatures are
  # generated across different platforms and Python key dictionaries.  The
  # resulting 'data' is a string encoded in UTF-8 and compatible with the input
  # expected by the cryptography functions called below.
  data = tuf.formats.encode_canonical(data)

  # Call the appropriate cryptography libraries for the supported key types,
  # otherwise raise an exception.
  if keytype == 'rsa':
    if _RSA_CRYPTO_LIBRARY == 'pycrypto':
      sig, method = tuf.pycrypto_keys.create_rsa_signature(private, data.encode('utf-8'))
    
    else: # pragma: no cover
      message = 'Unsupported "tuf.conf.RSA_CRYPTO_LIBRARY": ' +\
        repr(_RSA_CRYPTO_LIBRARY) + '.'
      raise tuf.UnsupportedLibraryError(message)
  
  elif keytype == 'ed25519':
    public = binascii.unhexlify(public.encode('utf-8'))
    private = binascii.unhexlify(private.encode('utf-8'))
    if 'pynacl' in _available_crypto_libraries:
      sig, method = tuf.ed25519_keys.create_signature(public, private, data.encode('utf-8'))
    
    else: # pragma: no cover
      message = 'The required PyNaCl library is unavailable.'
      raise tuf.UnsupportedLibraryError(message)

  # 'tuf.formats.ANYKEY_SCHEMA' should detect invalid key types. 
  else: # pragma: no cover
    raise TypeError('Invalid key type.')
    
  # Build the signature dictionary to be returned.
  # The hexadecimal representation of 'sig' is stored in the signature.
  signature['keyid'] = keyid
  signature['method'] = method
  signature['sig'] = binascii.hexlify(sig).decode()

  return signature
Esempio n. 7
0
def verify_signature(key_dict, signature, data):
  """
  <Purpose>
    Determine whether the private key belonging to 'key_dict' produced
    'signature'.  verify_signature() will use the public key found in
    'key_dict', the 'method' and 'sig' objects contained in 'signature',
    and 'data' to complete the verification.

    >>> ed25519_key = generate_ed25519_key()
    >>> data = 'The quick brown fox jumps over the lazy dog'
    >>> signature = create_signature(ed25519_key, data)
    >>> verify_signature(ed25519_key, signature, data)
    True
    >>> verify_signature(ed25519_key, signature, 'bad_data')
    False
    >>> rsa_key = generate_rsa_key()
    >>> signature = create_signature(rsa_key, data)
    >>> verify_signature(rsa_key, signature, data)
    True
    >>> verify_signature(rsa_key, signature, 'bad_data')
    False

  <Arguments>
    key_dict:
      A dictionary containing the TUF keys and other identifying information.
      If 'key_dict' is an RSA key, it has the form:
     
      {'keytype': 'rsa',
       'keyid': 'f30a0870d026980100c0573bd557394f8c1bbd6...',
       'keyval': {'public': '-----BEGIN RSA PUBLIC KEY----- ...',
                  'private': '-----BEGIN RSA PRIVATE KEY----- ...'}}

      The public and private keys are strings in PEM format.
      
    signature:
      The signature dictionary produced by one of the key generation functions.
      'signature' has the form:
      
      {'keyid': 'f30a0870d026980100c0573bd557394f8c1bbd6...',
       'method': 'method',
       'sig': sig}.
      
      Conformant to 'tuf.formats.SIGNATURE_SCHEMA'.
      
    data:
      Data object used by tuf.rsa_key.create_signature() to generate
      'signature'.  'data' is needed here to verify the signature.

  <Exceptions>
    tuf.FormatError, raised if either 'key_dict' or 'signature' are improperly
    formatted.
    
    tuf.UnsupportedLibraryError, if an unsupported or unavailable library is
    detected.
    
    tuf.UnknownMethodError.  Raised if the signing method used by
    'signature' is not one supported.

  <Side Effects>
    The cryptography library specified in 'tuf.conf' called to do the actual
    verification.

  <Returns>
    Boolean.  True if the signature is valid, False otherwise.
  """

  # Does 'key_dict' have the correct format?
  # This check will ensure 'key_dict' has the appropriate number
  # of objects and object types, and that all dict keys are properly named.
  # Raise 'tuf.FormatError' if the check fails.
  tuf.formats.ANYKEY_SCHEMA.check_match(key_dict)

  # Does 'signature' have the correct format?
  tuf.formats.SIGNATURE_SCHEMA.check_match(signature)
  
  # Using the public key belonging to 'key_dict'
  # (i.e., rsakey_dict['keyval']['public']), verify whether 'signature'
  # was produced by key_dict's corresponding private key
  # key_dict['keyval']['private'].
  method = signature['method']
  sig = signature['sig']
  sig = binascii.unhexlify(sig.encode('utf-8'))
  public = key_dict['keyval']['public']
  keytype = key_dict['keytype']
  valid_signature = False
  
  # Convert 'data' to canonical JSON format so that repeatable signatures are
  # generated across different platforms and Python key dictionaries.  The
  # resulting 'data' is a string encoded in UTF-8 and compatible with the input
  # expected by the cryptography functions called below.
  data = tuf.formats.encode_canonical(data).encode('utf-8')
  
  # Call the appropriate cryptography libraries for the supported key types,
  # otherwise raise an exception.
  if keytype == 'rsa':
    if _RSA_CRYPTO_LIBRARY == 'pycrypto':
      if 'pycrypto' not in _available_crypto_libraries: # pragma: no cover
        message = 'Metadata downloaded from the remote repository specified' +\
          ' an RSA signature.  Verifying RSA signatures requires PyCrypto.' +\
          '\n$ pip install PyCrypto, or pip install tuf[tools].'
        raise tuf.UnsupportedLibraryError(message)
      
      else:
        valid_signature = tuf.pycrypto_keys.verify_rsa_signature(sig, method,
                                                                 public, data) 
    else: # pragma: no cover
      message = 'Unsupported "tuf.conf.RSA_CRYPTO_LIBRARY": ' +\
        repr(_RSA_CRYPTO_LIBRARY)+'.'
      raise tuf.UnsupportedLibraryError(message) 
  
  elif keytype == 'ed25519':
    public = binascii.unhexlify(public.encode('utf-8'))
    if _ED25519_CRYPTO_LIBRARY == 'pynacl' or \
                              'pynacl' in _available_crypto_libraries:
      valid_signature = tuf.ed25519_keys.verify_signature(public,
                                                          method, sig, data,
                                                          use_pynacl=True)
    
    # Fall back to the optimized pure python implementation of ed25519. 
    else: # pragma: no cover
      valid_signature = tuf.ed25519_keys.verify_signature(public,
                                                          method, sig, data,
                                                          use_pynacl=False)
  
  # 'tuf.formats.ANYKEY_SCHEMA' should detect invalid key types. 
  else: # pragma: no cover
    raise TypeError('Unsupported key type.')

  return valid_signature 
Esempio n. 8
0
def generate_ed25519_key():
  """
  <Purpose> 
    Generate public and private ED25519 keys, both of length 32-bytes, although
    they are hexlified to 64 bytes.
    In addition, a keyid identifier generated for the returned ED25519 object.
    The object returned conforms to 'tuf.formats.ED25519KEY_SCHEMA' and has the
    form:
    {'keytype': 'ed25519',
     'keyid': 'f30a0870d026980100c0573bd557394f8c1bbd6...',
     'keyval': {'public': '9ccf3f02b17f82febf5dd3bab878b767d8408...',
                'private': 'ab310eae0e229a0eceee3947b6e0205dfab3...'}}
    
    The public and private keys are strings in PEM format and stored in the
    'keyval' field of the returned dictionary.

    >>> ed25519_key = generate_ed25519_key()
    >>> tuf.formats.ED25519KEY_SCHEMA.matches(ed25519_key)
    True
    >>> len(ed25519_key['keyval']['public'])
    64
    >>> len(ed25519_key['keyval']['private'])
    64

  <Arguments>
    None.
  
  <Exceptions>
    tuf.UnsupportedLibraryError, if an unsupported or unavailable library is
    detected.
  
  <Side Effects>
    The ED25519 keys are generated by calling either the optimized pure Python
    implementation of ed25519, or the ed25519 routines provided by 'pynacl'.

  <Returns>
    A dictionary containing the ED25519 keys and other identifying information.
    Conforms to 'tuf.formats.ED25519KEY_SCHEMA'. 
  """
  
  # Raise 'tuf.UnsupportedLibraryError' if the following libraries, specified
  # in 'tuf.conf', are unsupported or unavailable:
  # 'tuf.conf.ED25519_CRYPTO_LIBRARY'. 
  check_crypto_libraries(['ed25519'])

  # Begin building the ED25519 key dictionary. 
  ed25519_key = {}
  keytype = 'ed25519'
  public = None
  private = None

  # Generate the public and private ED25519 keys.  Use the 'pynacl' library
  # if available, otherwise fall back to optimized pure python implementation
  # provided by pyca and available in TUF.
  if 'pynacl' in _available_crypto_libraries:
    public, private = \
      tuf.ed25519_keys.generate_public_and_private()
  
  else: # pragma: no cover
    message = 'The required PyNaCl library is unavailable.' 
    raise tuf.UnsupportedLibraryError(message)

  # Generate the keyid of the ED25519 key.  'key_value' corresponds to the
  # 'keyval' entry of the 'ED25519KEY_SCHEMA' dictionary.  The private key
  # information is not included in the generation of the 'keyid' identifier.
  key_value = {'public': binascii.hexlify(public).decode(),
               'private': ''}
  keyid = _get_keyid(keytype, key_value)

  # Build the 'ed25519_key' dictionary.  Update 'key_value' with the ED25519
  # private key prior to adding 'key_value' to 'ed25519_key'.
  key_value['private'] = binascii.hexlify(private).decode()

  ed25519_key['keytype'] = keytype
  ed25519_key['keyid'] = keyid
  ed25519_key['keyval'] = key_value

  return ed25519_key
Esempio n. 9
0
def check_crypto_libraries(required_libraries):
  """
  <Purpose>
    Public function that ensures the cryptography libraries specified in
    'tuf.conf' are supported and available for each 'required_libraries'.

  <Arguments>
    required_libraries:
      A list of library strings to validate.  One, or multiple, strings from
      ['rsa', 'ed25519', 'general'] can be specified.

  <Exceptions>
    tuf.UnsupportedLibraryError, if the 'required_libraries' and the libraries
    specified in 'tuf.conf' are not supported or unavailable.

  <Side Effects>
    Validates the libraries set in 'tuf.conf'.

  <Returns>
    None.
  """
  
  # Does 'required_libraries' have the correct format?
  # This check will ensure 'required_libraries' has the appropriate number
  # of objects and object types, and that all dict keys are properly named.
  # Raise 'tuf.FormatError' if the check fails.
  tuf.formats.REQUIRED_LIBRARIES_SCHEMA.check_match(required_libraries)
 
  # The checks below all raise 'tuf.UnsupportedLibraryError' if the general,
  # RSA, and ED25519 crypto libraries specified in 'tuf.conf.py' are not
  # supported or unavailable.  The appropriate error message is added to the
  # exception.  The funcions of this module that depend on user-installed
  # crypto libraries should call this private function to ensure the called
  # routine does not fail with unpredictable exceptions in the event of a
  # missing library.  The supported and available lists checked are populated
  # when 'tuf.keys.py' is imported.
  
  if 'rsa' in required_libraries and _RSA_CRYPTO_LIBRARY not in \
                                   _SUPPORTED_RSA_CRYPTO_LIBRARIES:
    message = 'The ' + repr(_RSA_CRYPTO_LIBRARY) + ' crypto library specified' +\
      ' in "tuf.conf.RSA_CRYPTO_LIBRARY" is not supported.\n' +\
      'Supported crypto libraries: ' + repr(_SUPPORTED_RSA_CRYPTO_LIBRARIES) + '.'
    raise tuf.UnsupportedLibraryError(message)
  
  if 'ed25519' in required_libraries and _ED25519_CRYPTO_LIBRARY not in \
                                         _SUPPORTED_ED25519_CRYPTO_LIBRARIES:
    message = 'The '+repr(_ED25519_CRYPTO_LIBRARY)+' crypto library specified'+\
      ' in "tuf.conf.ED25519_CRYPTO_LIBRARY" is not supported.\n'+ \
      'Supported crypto libraries: '+repr(_SUPPORTED_ED25519_CRYPTO_LIBRARIES)+'.'
    raise tuf.UnsupportedLibraryError(message)
  
  if 'general' in required_libraries and _GENERAL_CRYPTO_LIBRARY not in \
                                         _SUPPORTED_GENERAL_CRYPTO_LIBRARIES:
    message = 'The '+repr(_GENERAL_CRYPTO_LIBRARY)+' crypto library specified'+\
      ' in "tuf.conf.GENERAL_CRYPTO_LIBRARY" is not supported.\n'+ \
      'Supported crypto libraries: '+repr(_SUPPORTED_GENERAL_CRYPTO_LIBRARIES)+'.'
    raise tuf.UnsupportedLibraryError(message)

  if 'rsa' in required_libraries and _RSA_CRYPTO_LIBRARY not in \
                                     _available_crypto_libraries:
    message = 'The '+repr(_RSA_CRYPTO_LIBRARY)+' crypto library specified'+ \
      ' in "tuf.conf.RSA_CRYPTO_LIBRARY" could not be imported.'
    raise tuf.UnsupportedLibraryError(message)
  
  if 'ed25519' in required_libraries and _ED25519_CRYPTO_LIBRARY not in \
                                         _available_crypto_libraries:
    message = 'The '+repr(_ED25519_CRYPTO_LIBRARY)+' crypto library specified'+\
      ' in "tuf.conf.ED25519_CRYPTO_LIBRARY" could not be imported.'
    raise tuf.UnsupportedLibraryError(message)
  
  if 'general' in required_libraries and _GENERAL_CRYPTO_LIBRARY not in \
                                         _available_crypto_libraries:
    message = 'The '+repr(_GENERAL_CRYPTO_LIBRARY)+' crypto library specified'+\
      ' in "tuf.conf.GENERAL_CRYPTO_LIBRARY" could not be imported.'
    raise tuf.UnsupportedLibraryError(message)
Esempio n. 10
0
def create_rsa_encrypted_pem(private_key, passphrase):
  """
  <Purpose>
  Return a string in PEM format, where the private part of the RSA key is
  encrypted. The private part of the RSA key is encrypted by the Triple
  Data Encryption Algorithm (3DES) and Cipher-block chaining (CBC) for the
  mode of operation. Password-Based Key Derivation Function 1 (PBKF1) + MD5
  is used to strengthen 'passphrase'.

  https://en.wikipedia.org/wiki/Triple_DES
  https://en.wikipedia.org/wiki/PBKDF2

  >>> rsa_key = generate_rsa_key()
  >>> private = rsa_key['keyval']['private']
  >>> passphrase = 'secret'
  >>> encrypted_pem = create_rsa_encrypted_pem(private, passphrase)
  >>> tuf.formats.PEMRSA_SCHEMA.matches(encrypted_pem)
  True

  <Arguments>
  private_key:
  The private key string in PEM format.

  passphrase:
  The passphrase, or password, to encrypt the private part of the RSA
  key. 'passphrase' is not used directly as the encryption key, a stronger
  encryption key is derived from it.

  <Exceptions>
  tuf.FormatError, if the arguments are improperly formatted.

  tuf.CryptoError, if an RSA key in encrypted PEM format cannot be created.

  TypeError, 'private_key' is unset.

  <Side Effects>
  PyCrypto's Crypto.PublicKey.RSA.exportKey() called to perform the actual
  generation of the PEM-formatted output.

  <Returns>
  A string in PEM format, where the private RSA key is encrypted.
  Conforms to 'tuf.formats.PEMRSA_SCHEMA'.
  """
    
  # Does 'private_key' have the correct format?
  # This check will ensure 'private_key' has the appropriate number
  # of objects and object types, and that all dict keys are properly named.
  # Raise 'tuf.FormatError' if the check fails.
  tuf.formats.PEMRSA_SCHEMA.check_match(private_key)

  # Does 'passphrase' have the correct format?
  tuf.formats.PASSWORD_SCHEMA.check_match(passphrase)
  
  # Raise 'tuf.UnsupportedLibraryError' if the following libraries, specified in
  # 'tuf.conf', are unsupported or unavailable:
  # 'tuf.conf.GENERAL_CRYPTO_LIBRARY' and 'tuf.conf.RSA_CRYPTO_LIBRARY'.
  check_crypto_libraries(['rsa', 'general'])

  encrypted_pem = None

  # Generate the public and private RSA keys. The PyCrypto module performs
  # the actual key generation. Raise 'ValueError' if 'bits' is less than 1024
  # or not a multiple of 256, although a 2048-bit minimum is enforced by
  # tuf.formats.RSAKEYBITS_SCHEMA.check_match().
  if _RSA_CRYPTO_LIBRARY == 'pycrypto':
    encrypted_pem = \
      tuf.pycrypto_keys.create_rsa_encrypted_pem(private_key, passphrase)
 
  # check_crypto_libraries() should have fully verified _RSA_CRYPTO_LIBRARY.
  else: # pragma: no cover
    message = 'Invalid crypto library: ' + repr(_RSA_CRYPTO_LIBRARY) + '.'
    raise tuf.UnsupportedLibraryError(message)

  return encrypted_pem
Esempio n. 11
0
def generate_rsa_key(bits=_DEFAULT_RSA_KEY_BITS):
  """
  <Purpose> 
    Generate public and private RSA keys, with modulus length 'bits'.  In
    addition, a keyid identifier for the RSA key is generated.  The object
    returned conforms to 'tuf.formats.RSAKEY_SCHEMA' and has the
    form:
    
    {'keytype': 'rsa',
     'keyid': keyid,
     'keyval': {'public': '-----BEGIN RSA PUBLIC KEY----- ...',
                'private': '-----BEGIN RSA PRIVATE KEY----- ...'}}
    
    The public and private keys are strings in PEM format.

    Although the PyCrypto crytography library called sets a 1024-bit minimum
    key size, generate() enforces a minimum key size of 2048 bits.  If 'bits' is
    unspecified, a 3072-bit RSA key is generated, which is the key size
    recommended by TUF. 
    
    >>> rsa_key = generate_rsa_key(bits=2048)
    >>> tuf.formats.RSAKEY_SCHEMA.matches(rsa_key)
    True
    >>> public = rsa_key['keyval']['public']
    >>> private = rsa_key['keyval']['private']
    >>> tuf.formats.PEMRSA_SCHEMA.matches(public)
    True
    >>> tuf.formats.PEMRSA_SCHEMA.matches(private)
    True
  
  <Arguments>
    bits:
      The key size, or key length, of the RSA key.  'bits' must be 2048, or
      greater, and a multiple of 256.

  <Exceptions>
    tuf.FormatError, if 'bits' is improperly or invalid (i.e., not an integer
    and not at least 2048).
   
    tuf.UnsupportedLibraryError, if any of the cryptography libraries specified
    in 'tuf.conf.py' are unsupported or unavailable.

    ValueError, if an exception occurs after calling the RSA key generation
    routine.  'bits' must be a multiple of 256.  The 'ValueError' exception is
    raised by the key generation function of the cryptography library called.

  <Side Effects>
    The RSA keys are generated by calling PyCrypto's
    Crypto.PublicKey.RSA.generate().

  <Returns>
    A dictionary containing the RSA keys and other identifying information.
    Conforms to 'tuf.formats.RSAKEY_SCHEMA'. 
  """

  # Does 'bits' have the correct format?
  # This check will ensure 'bits' conforms to 'tuf.formats.RSAKEYBITS_SCHEMA'.
  # 'bits' must be an integer object, with a minimum value of 2048.
  # Raise 'tuf.FormatError' if the check fails.
  tuf.formats.RSAKEYBITS_SCHEMA.check_match(bits)

  # Raise 'tuf.UnsupportedLibraryError' if the following libraries, specified in
  # 'tuf.conf', are unsupported or unavailable: 'tuf.conf.RSA_CRYPTO_LIBRARY'. 
  check_crypto_libraries(['rsa'])

  # Begin building the RSA key dictionary. 
  rsakey_dict = {}
  keytype = 'rsa'
  public = None
  private = None

  # Generate the public and private RSA keys.  The PyCrypto module performs
  # the actual key generation.  Raise 'ValueError' if 'bits' is less than 1024 
  # or not a multiple of 256, although a 2048-bit minimum is enforced by
  # tuf.formats.RSAKEYBITS_SCHEMA.check_match().
  if _RSA_CRYPTO_LIBRARY == 'pycrypto':
    public, private = tuf.pycrypto_keys.generate_rsa_public_and_private(bits)
  
  else: # pragma: no cover
    message = 'Invalid crypto library: ' + repr(_RSA_CRYPTO_LIBRARY) + '.'
    raise tuf.UnsupportedLibraryError(message) 
    
  # Generate the keyid of the RSA key.  'key_value' corresponds to the
  # 'keyval' entry of the 'RSAKEY_SCHEMA' dictionary.  The private key
  # information is not included in the generation of the 'keyid' identifier.
  key_value = {'public': public,
               'private': ''}
  keyid = _get_keyid(keytype, key_value)

  # Build the 'rsakey_dict' dictionary.  Update 'key_value' with the RSA
  # private key prior to adding 'key_value' to 'rsakey_dict'.
  key_value['private'] = private

  rsakey_dict['keytype'] = keytype
  rsakey_dict['keyid'] = keyid
  rsakey_dict['keyval'] = key_value

  return rsakey_dict
Esempio n. 12
0
def decrypt_key(encrypted_key, passphrase):
  """
  <Purpose>
    Return a string containing 'encrypted_key' in non-encrypted form.
    The decrypt_key() function can be applied to the encrypted string to restore
    the original key object, a TUF key (e.g., RSAKEY_SCHEMA, ED25519KEY_SCHEMA).
    This function calls the appropriate cryptography module (e.g.,
    pycrypto_keys.py) to perform the decryption.

    The currently supported general-purpose crypto module, 'pycrypto_keys.py', 
    performs the actual cryptographic operation on 'key_object'.  Whereas
    an encrypted PEM file uses the Triple Data Encryption Algorithm (3DES), the
    Cipher-block chaining (CBC) mode of operation, and the Password-Based Key
    Derivation Function 1 (PBKF1) + MD5 to strengthen 'password', encrypted
    TUF keys use AES-256-CTR-Mode and passwords strengthened with
    PBKDF2-HMAC-SHA256 (100K iterations be default, but may be overriden in
    'tuf.conf.py' by the user).

    http://en.wikipedia.org/wiki/Advanced_Encryption_Standard
    http://en.wikipedia.org/wiki/CTR_mode#Counter_.28CTR.29
    https://en.wikipedia.org/wiki/PBKDF2

    >>> ed25519_key = generate_ed25519_key()
    >>> password = '******'
    >>> encrypted_key = encrypt_key(ed25519_key, password)
    >>> decrypted_key = decrypt_key(encrypted_key.encode('utf-8'), password)
    >>> tuf.formats.ANYKEY_SCHEMA.matches(decrypted_key)
    True
    >>> decrypted_key == ed25519_key
    True

  <Arguments>
    encrypted_key:
      An encrypted TUF key (additional data is also included, such as salt,
      number of password iterations used for the derived encryption key, etc)
      of the form 'tuf.formats.ENCRYPTEDKEY_SCHEMA'.  'encrypted_key' should
      have been generated with encrypted_key().

    password:
      The password, or passphrase, to decrypt 'encrypted_key'.  'password' is
      not used directly as the encryption key, a stronger encryption key is
      derived from it.  The supported general-purpose module takes care of
      re-deriving the encryption key.

  <Exceptions>
    tuf.FormatError, if the arguments are improperly formatted.

    tuf.CryptoError, if 'encrypted_key' cannot be decrypted.

    tuf.UnsupportedLibraryError, if the general-purpose cryptography library
    specified in 'tuf.conf.GENERAL_CRYPTO_LIBRARY' is unsupported.

  <Side Effects>
    Perform crytographic operations using the library specified in
    'tuf.formats.GENERAL_CRYPTO_LIBRARY' and 'password'.

  <Returns>
    A TUF key object of the form: 'tuf.formats.ANYKEY_SCHEMA' (e.g.,
    RSAKEY_SCHEMA, ED25519KEY_SCHEMA).
  """
  
  # Does 'encrypted_key' have the correct format?
  # This check ensures 'encrypted_key' has the appropriate number
  # of objects and object types, and that all dict keys are properly named.
  # Raise 'tuf.FormatError' if the check fails.
  tuf.formats.ENCRYPTEDKEY_SCHEMA.check_match(encrypted_key)
  
  # Does 'passphrase' have the correct format?
  tuf.formats.PASSWORD_SCHEMA.check_match(passphrase)
  
  # Raise 'tuf.UnsupportedLibraryError' if the following libraries, specified in
  # 'tuf.conf', are unsupported or unavailable:
  # 'tuf.conf.GENERAL_CRYPTO_LIBRARY'. 
  check_crypto_libraries(['general'])

  # Store and return the decrypted key object.
  key_object = None

  # Decrypt 'encrypted_key' so that the original key object is restored.
  # encrypt_key() generates an encrypted string of the TUF key object using
  # AES-256-CTR-Mode, where 'password' is strengthened with PBKDF2-HMAC-SHA256.
  # Ensure the general-purpose library specified in
  # 'tuf.conf.GENERAL_CRYPTO_LIBRARY' is supported.
  if _GENERAL_CRYPTO_LIBRARY == 'pycrypto':
    key_object = \
      tuf.pycrypto_keys.decrypt_key(encrypted_key, passphrase)
  
  # check_crypto_libraries() should have fully verified _GENERAL_CRYPTO_LIBRARY.
  else: # pragma: no cover
    message = 'Invalid crypto library: ' + repr(_GENERAL_CRYPTO_LIBRARY) + '.'
    raise tuf.UnsupportedLibraryError(message) 

  # The corresponding encrypt_key() encrypts and stores key objects in
  # non-metadata format (i.e., original format of key object argument to
  # encrypt_key()) prior to returning.
  
  return key_object
Esempio n. 13
0
def encrypt_key(key_object, password):
  """
  <Purpose>
    Return a string containing 'key_object' in encrypted form. Encrypted strings
    may be safely saved to a file.  The corresponding decrypt_key() function can
    be applied to the encrypted string to restore the original key object.
    'key_object' is a TUF key (e.g., RSAKEY_SCHEMA, ED25519KEY_SCHEMA).  This
    function calls the appropriate cryptography module (e.g., pycrypto_keys.py)
    to perform the encryption.
    
    The currently supported general-purpose crypto module, 'pycrypto_keys.py', 
    performs the actual cryptographic operation on 'key_object'.  Whereas
    an encrypted PEM file uses the Triple Data Encryption Algorithm (3DES), the
    Cipher-block chaining (CBC) mode of operation, and the Password-Based Key
    Derivation Function 1 (PBKF1) + MD5 to strengthen 'password', encrypted
    TUF keys use AES-256-CTR-Mode and passwords strengthened with
    PBKDF2-HMAC-SHA256 (100K iterations by default, but may be overriden in
    'tuf.conf.PBKDF2_ITERATIONS' by the user).

    http://en.wikipedia.org/wiki/Advanced_Encryption_Standard
    http://en.wikipedia.org/wiki/CTR_mode#Counter_.28CTR.29
    https://en.wikipedia.org/wiki/PBKDF2

    >>> ed25519_key = generate_ed25519_key()
    >>> password = '******'
    >>> encrypted_key = encrypt_key(ed25519_key, password).encode('utf-8')
    >>> tuf.formats.ENCRYPTEDKEY_SCHEMA.matches(encrypted_key)
    True

  <Arguments>
    key_object:
      A TUF key (containing also the private key portion) of the form
      'tuf.formats.ANYKEY_SCHEMA'

    password:
      The password, or passphrase, to encrypt the private part of the RSA
      key.  'password' is not used directly as the encryption key, a stronger
      encryption key is derived from it. 

  <Exceptions>
    tuf.FormatError, if the arguments are improperly formatted.

    tuf.CryptoError, if 'key_object' cannot be encrypted.

    tuf.UnsupportedLibraryError, if the general-purpose cryptography library
    specified in 'tuf.conf.GENERAL_CRYPTO_LIBRARY' is unsupported.

  <Side Effects>
    Perform crytographic operations using the library specified in
    'tuf.formats.GENERAL_CRYPTO_LIBRARY' and 'password'.

  <Returns>
    An encrypted string of the form: 'tuf.formats.ENCRYPTEDKEY_SCHEMA'.
  """
  
  # Does 'key_object' have the correct format?
  # This check will ensure 'key_object' has the appropriate number
  # of objects and object types, and that all dict keys are properly named.
  # Raise 'tuf.FormatError' if the check fails.
  tuf.formats.ANYKEY_SCHEMA.check_match(key_object)
  
  # Does 'password' have the correct format?
  tuf.formats.PASSWORD_SCHEMA.check_match(password)
  
  # Raise 'tuf.UnsupportedLibraryError' if the following libraries, specified in
  # 'tuf.conf', are unsupported or unavailable:
  # 'tuf.conf.GENERAL_CRYPTO_LIBRARY'. 
  check_crypto_libraries(['general'])

  # Encrypted string of 'key_object'.  The encrypted string may be safely saved
  # to a file and stored offline.
  encrypted_key = None

  # Generate an encrypted string of 'key_object' using AES-256-CTR-Mode, where
  # 'password' is strengthened with PBKDF2-HMAC-SHA256.  Ensure the general-
  # purpose library specified in 'tuf.conf.GENERAL_CRYPTO_LIBRARY' is supported.
  if _GENERAL_CRYPTO_LIBRARY == 'pycrypto':
    encrypted_key = \
      tuf.pycrypto_keys.encrypt_key(key_object, password)
 
  # check_crypto_libraries() should have fully verified _GENERAL_CRYPTO_LIBRARY.
  else: # pragma: no cover
    message = 'Invalid crypto library: ' + repr(_GENERAL_CRYPTO_LIBRARY) + '.'
    raise tuf.UnsupportedLibraryError(message) 

  return encrypted_key
Esempio n. 14
0
# Import the python implementation of the ed25519 algorithm that is provided by
# the author.  Note: This implementation is very slow and does not include
# protection against side-channel attacks according to the author.  Verifying
# signatures can take approximately 9 seconds on an intel core 2 duo @
# 2.2 ghz x 2).  Optionally, the PyNaCl module may be used to speed up ed25519
# cryptographic operations.
# http://ed25519.cr.yp.to/software.html
# Try to import PyNaCl.  The functions found in this module provide the option
# of using PyNaCl over the slower implementation of ed25519.
try:
    import nacl.signing
    import nacl.encoding
except (ImportError, IOError):
    message = 'The PyNacl library and/or its dependencies cannot be imported.'
    raise tuf.UnsupportedLibraryError(message)

# The pure Python implementation of ed25519.
import ed25519.ed25519

# Digest objects needed to generate hashes.
import tuf.hash

# Perform object format-checking.
import tuf.formats

# The default hash algorithm to use when generating KeyIDs.
_KEY_ID_HASH_ALGORITHM = 'sha256'

# Supported ed25519 signing methods.  'ed25519-python' is the pure Python
# implementation signing method.  'ed25519-pynacl' (i.e., 'nacl' module) is the
Esempio n. 15
0
def generate_public_and_private(use_pynacl=False):
  """
  <Purpose> 
    Generate a pair of ed25519 public and private keys.
    The public and private keys returned conform to
    'tuf.formats.ED25519PULIC_SCHEMA' and 'tuf.formats.ED25519SEED_SCHEMA',
    respectively, and have the form:
    
    '\xa2F\x99\xe0\x86\x80%\xc8\xee\x11\xb95T\xd9\...'

    An ed25519 seed key is a random 32-byte string.  Public keys are also 32
    bytes.

    >>> public, private = generate_public_and_private(use_pynacl=False)
    >>> tuf.formats.ED25519PUBLIC_SCHEMA.matches(public)
    True
    >>> tuf.formats.ED25519SEED_SCHEMA.matches(private)
    True
    >>> public, private = generate_public_and_private(use_pynacl=True)
    >>> tuf.formats.ED25519PUBLIC_SCHEMA.matches(public)
    True
    >>> tuf.formats.ED25519SEED_SCHEMA.matches(private)
    True

  <Arguments>
    use_pynacl:
      True, if the ed25519 keys should be generated with PyNaCl.  False, if the
      keys should be generated with the pure Python implementation of ed25519
      (slower).

  <Exceptions>
    tuf.FormatError, if 'use_pynacl' is not a Boolean.

    tuf.UnsupportedLibraryError, if the PyNaCl ('nacl') module is unavailable
    and 'use_pynacl' is True. 

    NotImplementedError, if a randomness source is not found by 'os.urandom'.

  <Side Effects>
    The ed25519 keys are generated by first creating a random 32-byte seed
    with os.urandom() and then calling ed25519's
    ed25519.25519.publickey(seed) or PyNaCl's nacl.signing.SigningKey().

  <Returns>
    A (public, private) tuple that conform to 'tuf.formats.ED25519PUBLIC_SCHEMA'
    and 'tuf.formats.ED25519SEED_SCHEMA', respectively.
  """
  
  # Does 'use_pynacl' have the correct format?
  # This check will ensure 'use_pynacl' conforms to 'tuf.formats.TOGGLE_SCHEMA'.
  # Raise 'tuf.FormatError' if the check fails.
  tuf.formats.TOGGLE_SCHEMA.check_match(use_pynacl)

  # Generate ed25519's seed key by calling os.urandom().  The random bytes
  # returned should be suitable for cryptographic use and is OS-specific.
  # Raise 'NotImplementedError' if a randomness source is not found.
  # ed25519 seed keys are fixed at 32 bytes (256-bit keys).
  # http://blog.mozilla.org/warner/2011/11/29/ed25519-keys/ 
  seed = os.urandom(32)
  public = None

  if use_pynacl:
    # Generate the public key.  PyNaCl (i.e., 'nacl' module) performs
    # the actual key generation.
    try:
      nacl_key = nacl.signing.SigningKey(seed)
      public = str(nacl_key.verify_key)
    except NameError:
      message = 'The PyNaCl library and/or its dependencies unavailable.'
      raise tuf.UnsupportedLibraryError(message)

  # Use the pure Python implementation of ed25519. 
  else: 
    public = ed25519.ed25519.publickey(seed)
  
  return public, seed
Esempio n. 16
0
def create_signature(public_key, private_key, data, use_pynacl=False):
  """
  <Purpose>
    Return a (signature, method) tuple, where the method is either:
    'ed25519-python' if the signature is generated by the pure python
    implemenation, or 'ed25519-pynacl' if generated by 'nacl'.
    signature conforms to 'tuf.formats.ED25519SIGNATURE_SCHEMA', and has the
    form:
    
    '\xae\xd7\x9f\xaf\x95{bP\x9e\xa8YO Z\x86\x9d...'

    A signature is a 64-byte string.

    >>> public, private = generate_public_and_private(use_pynacl=False)
    >>> data = 'The quick brown fox jumps over the lazy dog'
    >>> signature, method = \
        create_signature(public, private, data, use_pynacl=False)
    >>> tuf.formats.ED25519SIGNATURE_SCHEMA.matches(signature)
    True
    >>> method == 'ed25519-python'
    True
    >>> signature, method = \
        create_signature(public, private, data, use_pynacl=True)
    >>> tuf.formats.ED25519SIGNATURE_SCHEMA.matches(signature)
    True
    >>> method == 'ed25519-pynacl'
    True

  <Arguments>
    public:
      The ed25519 public key, which is a 32-byte string.
    
    private:
      The ed25519 private key, which is a 32-byte string.

    data:
      Data object used by create_signature() to generate the signature.
    
    use_pynacl:
      True, if the ed25519 signature should be generated with PyNaCl.  False,
      if the signature should be generated with the pure Python implementation
      of ed25519 (much slower).

  <Exceptions>
    tuf.FormatError, if the arguments are improperly formatted.

    tuf.CryptoError, if a signature cannot be created.

  <Side Effects>
    ed25519.ed25519.signature() or nacl.signing.SigningKey.sign() called to
    generate the actual signature.

  <Returns>
    A signature dictionary conformat to 'tuf.format.SIGNATURE_SCHEMA'.
    ed25519 signatures are 64 bytes, however, the hexlified signature is
    stored in the dictionary returned.
  """
  
  # Does 'public_key' have the correct format?
  # This check will ensure 'public_key' conforms to
  # 'tuf.formats.ED25519PUBLIC_SCHEMA', which must have length 32 bytes.
  # Raise 'tuf.FormatError' if the check fails.
  tuf.formats.ED25519PUBLIC_SCHEMA.check_match(public_key)

  # Is 'private_key' properly formatted?
  tuf.formats.ED25519SEED_SCHEMA.check_match(private_key)
  
  # Is 'use_pynacl' properly formatted?
  tuf.formats.TOGGLE_SCHEMA.check_match(use_pynacl)
  
  # Signing the 'data' object requires a seed and public key.
  # 'ed25519.ed25519.py' generates the actual 64-byte signature in pure Python.
  # nacl.signing.SigningKey.sign() generates the signature if 'use_pynacl'
  # is True.
  public = public_key
  private = private_key

  method = None 
  signature = None
 
  # The private and public keys have been validated above by 'tuf.formats' and
  # should be 32-byte strings.
  if use_pynacl:
    method = 'ed25519-pynacl'
    try:
      nacl_key = nacl.signing.SigningKey(private)
      nacl_sig = nacl_key.sign(data)
      signature = nacl_sig.signature
    
    except NameError:
      message = 'The PyNaCl library and/or its dependencies unavailable.'
      raise tuf.UnsupportedLibraryError(message)
    
    except (ValueError, nacl.signing.CryptoError):
      message = 'An "ed25519-pynacl" signature could not be created.'
      raise tuf.CryptoError(message)
   
  # Generate an "ed25519-python" (i.e., pure python implementation) signature.
  else:
    # ed25519.ed25519.signature() requires both the seed and public keys.
    # It calculates the SHA512 of the seed key, which is 32 bytes.
    method = 'ed25519-python'
    try:
      signature = ed25519.ed25519.signature(data, private, public)
   
    # 'Exception' raised by ed25519.py for any exception that may occur.
    except Exception, e:
      message = 'An "ed25519-python" signature could not be generated.'
      raise tuf.CryptoError(message)