示例#1
0
def check_signable_object_format(signable):
    """
  <Purpose>
    Ensure 'signable' is properly formatted, conformant to
    'SIGNABLE_SCHEMA'.  Return the signing role on
    success.  Note: The 'signed' field of a 'SIGNABLE_SCHEMA' is checked
    against securesystemslib.schema.Any().  The 'signed' field, however, should
    actually hold one of the supported role schemas (e.g., 'ROOT_SCHEMA',
    'TARGETS_SCHEMA').  The role schemas all differ in their format, so this
    function determines exactly which schema is listed in the 'signed' field.

  <Arguments>
    signable:
     The signable object compared against 'SIGNABLE.SCHEMA'.

  <Exceptions>
    securesystemslib.exceptions.FormatError, if 'signable' does not have the
    correct format.

    tuf.exceptions.UnsignedMetadataError, if 'signable' does not have any
    signatures

  <Side Effects>
    None.

  <Returns>
    A string representing the signing role (e.g., 'root', 'targets').
    The role string is returned with characters all lower case.
  """

    # Does 'signable' have the correct type?
    # This check ensures 'signable' conforms to
    # 'SIGNABLE_SCHEMA'.
    SIGNABLE_SCHEMA.check_match(signable)

    try:
        role_type = signable['signed']['_type']

    except (KeyError, TypeError) as error:
        six.raise_from(
            sslib_exceptions.FormatError('Untyped signable object.'), error)

    try:
        schema = SCHEMAS_BY_TYPE[role_type]

    except KeyError as error:
        six.raise_from(
            sslib_exceptions.FormatError('Unrecognized type ' +
                                         repr(role_type)), error)

    if not signable['signatures']:
        raise exceptions.UnsignedMetadataError(
            'Signable object of type ' + repr(role_type) +
            ' has no signatures ', signable)

    # 'securesystemslib.exceptions.FormatError' raised if 'signable' does not
    # have a properly formatted role schema.
    schema.check_match(signable['signed'])

    return role_type.lower()
示例#2
0
def parse_base64(base64_string):
    """
  <Purpose>
    Parse a base64 encoding with whitespace and '=' signs omitted.

  <Arguments>
    base64_string:
      A string holding a base64 value.

  <Exceptions>
    securesystemslib.exceptions.FormatError, if 'base64_string' cannot be parsed
    due to an invalid base64 encoding.

  <Side Effects>
    None.

  <Returns>
    A byte string representing the parsed based64 encoding of
    'base64_string'.
  """

    if not isinstance(base64_string, str):
        message = 'Invalid argument: ' + repr(base64_string)
        raise exceptions.FormatError(message)

    extra = len(base64_string) % 4
    if extra:
        padding = '=' * (4 - extra)
        base64_string = base64_string + padding

    try:
        return binascii.a2b_base64(base64_string.encode('utf-8'))

    except (TypeError, binascii.Error) as e:
        raise exceptions.FormatError('Invalid base64' ' encoding: ' + str(e))
示例#3
0
    def check_match(self, object):
        if not isinstance(object, dict):
            raise exceptions.FormatError('Wanted a ' +
                                         repr(self._object_name) + '.')

        # (key, schema) = (a, AnyString()) = (a=AnyString())
        for key, schema in self._required:
            # Check if 'object' has all the required dict keys.  If not one of the
            # required keys, check if it is an Optional().
            try:
                item = object[key]

            except KeyError:
                # If not an Optional schema, raise an exception.
                if not isinstance(schema, Optional):
                    raise exceptions.FormatError('Missing key ' + repr(key) +
                                                 ' in ' +
                                                 repr(self._object_name))

            # Check that 'object's schema matches Object()'s schema for this
            # particular 'key'.
            else:
                try:
                    schema.check_match(item)

                except exceptions.FormatError as e:
                    raise exceptions.FormatError(
                        str(e) + ' in ' + self._object_name + '.' + key)
示例#4
0
    def check_match(self, object):
        if not isinstance(object, (list, tuple)):
            raise exceptions.FormatError('Expected ' +
                                         repr(self._struct_name) +
                                         '; but got ' + repr(object))

        elif len(object) < self._min:
            raise exceptions.FormatError('Too few fields in ' +
                                         self._struct_name)

        elif len(object) > len(self._sub_schemas) and not self._allow_more:
            raise exceptions.FormatError('Too many fields in ' +
                                         self._struct_name)

        # Iterate through the items of 'object', checking against each schema in
        # the list of schemas allowed (i.e., the sub-schemas and also any optional
        # schemas.  The lenth of 'object' must be less than the length of the
        # required schemas + the optional schemas.  However, 'object' is allowed to
        # be only as large as the length of the required schemas.  In the while
        # loop below, we check against these two cases.
        index = 0
        while index < len(object) and index < len(self._sub_schemas):
            item = object[index]
            schema = self._sub_schemas[index]
            schema.check_match(item)
            index = index + 1
示例#5
0
    def __init__(self,
                 sub_schemas,
                 optional_schemas=None,
                 allow_more=False,
                 struct_name='list'):
        """
    <Purpose>
      Create a new Struct schema.

    <Arguments>
      sub_schemas: The sub-schemas recognized.
      optional_schemas: Optional list. If none is given, it will be "[]".
      allow_more: Specifies that an optional list of types is allowed.
      struct_name: A string identifier for the Struct object.
    """

        if optional_schemas is None:
            optional_schemas = []

        # Ensure each item of the list contains the expected object type.
        if not isinstance(sub_schemas, (list, tuple)):
            raise exceptions.FormatError('Expected Schema but got ' +
                                         repr(sub_schemas))

        for schema in sub_schemas:
            if not isinstance(schema, Schema):
                raise exceptions.FormatError('Expected Schema but'
                                             ' got ' + repr(schema))

        self._sub_schemas = sub_schemas + optional_schemas
        self._min = len(sub_schemas)
        self._allow_more = allow_more
        self._struct_name = struct_name
示例#6
0
    def check_match(self, object):
        if not isinstance(object, six.binary_type):
            raise exceptions.FormatError('Expected a byte but'
                                         ' got ' + repr(object))

        if len(object) != self._bytes_length:
            raise exceptions.FormatError('Expected a byte of'
                                         ' length ' + repr(self._bytes_length))
示例#7
0
    def check_match(self, object):
        if not isinstance(object, six.string_types):
            raise exceptions.FormatError('Expected a string but'
                                         ' got ' + repr(object))

        if len(object) != self._string_length:
            raise exceptions.FormatError('Expected a string of'
                                         ' length ' +
                                         repr(self._string_length))
示例#8
0
def import_publickeys_from_file(filepaths, key_types=None):
    """Imports multiple public keys from files.

  NOTE: The default signing scheme 'rsassa-pss-sha256' is assigned to RSA keys.
  Use 'import_rsa_publickey_from_file' to specify any other than the default
  signing scheme for an RSA key. ed25519 and ecdsa keys have the signing scheme
  included in the custom key format (see generate functions).

  Arguments:
    filepaths: A list of paths to public key files.
    key_types (optional): A list of types of keys to be imported associated
        with filepaths by index. Must be one of KEY_TYPE_RSA, KEY_TYPE_ED25519
        or KEY_TYPE_ECDSA. If not specified, all keys are assumed to be
        KEY_TYPE_RSA.

  Raises:
    TypeError: filepaths or 'key_types' (if passed) is not iterable.
    FormatError: Argument are malformed, or 'key_types' is passed and does not
        have the same length as 'filepaths' or contains an unsupported type.
    UnsupportedLibraryError: pyca/cryptography is not available.
    StorageError: Key file cannot be read.
    Error: Public key is malformed.

  Returns:
    A dict of public keys in KEYDICT_SCHEMA format.

  """
    if key_types is None:
        key_types = [KEY_TYPE_RSA] * len(filepaths)

    if len(key_types) != len(filepaths):
        raise exceptions.FormatError(
            "Pass equal amount of 'filepaths' (got {}) and 'key_types (got {}), "
            "or no 'key_types' at all to default to '{}'.".format(
                len(filepaths), len(key_types), KEY_TYPE_RSA))

    key_dict = {}
    for idx, filepath in enumerate(filepaths):
        if key_types[idx] == KEY_TYPE_ED25519:
            key = import_ed25519_publickey_from_file(filepath)

        elif key_types[idx] == KEY_TYPE_RSA:
            key = import_rsa_publickey_from_file(filepath)

        elif key_types[idx] == KEY_TYPE_ECDSA:
            key = import_ecdsa_publickey_from_file(filepath)

        else:
            raise exceptions.FormatError(
                "Unsupported key type '{}'. Must be '{}', '{}' or '{}'.".
                format(key_types[idx], KEY_TYPE_RSA, KEY_TYPE_ED25519,
                       KEY_TYPE_ECDSA))

        key_dict[key["keyid"]] = key

    return key_dict
示例#9
0
    def check_match(self, object):
        if isinstance(object, bool) or not isinstance(object, int):
            # We need to check for bool as a special case, since bool
            # is for historical reasons a subtype of int.
            raise exceptions.FormatError('Got ' + repr(object) +
                                         ' instead of an integer.')

        elif not (self._lo <= object <= self._hi):
            int_range = '[' + repr(self._lo) + ', ' + repr(self._hi) + '].'
            raise exceptions.FormatError(
                repr(object) + ' not in range ' + int_range)
示例#10
0
    def __init__(self, required_schemas):
        # Ensure each item of the list contains the expected object type.
        if not isinstance(required_schemas, list):
            raise exceptions.FormatError('Expected a list but'
                                         ' got' + repr(required_schemas))

        for schema in required_schemas:
            if not isinstance(schema, Schema):
                raise exceptions.FormatError('List contains an'
                                             ' invalid item ' + repr(schema))

        self._required_schemas = required_schemas[:]
示例#11
0
    def __init__(self, alternatives):
        # Ensure each item of the list contains the expected object type.
        if not isinstance(alternatives, list):
            raise exceptions.FormatError('Expected a list but'
                                         ' got ' + repr(alternatives))

        for alternative in alternatives:
            if not isinstance(alternative, Schema):
                raise exceptions.FormatError('List contains an'
                                             ' invalid item ' +
                                             repr(alternative))

        self._alternatives = alternatives
示例#12
0
文件: formats.py 项目: sechkova/tuf
def format_base64(data):
    """
  <Purpose>
    Return the base64 encoding of 'data' with whitespace and '=' signs omitted.

  <Arguments>
    data:
      Binary or buffer of data to convert.

  <Exceptions>
    securesystemslib.exceptions.FormatError, if the base64 encoding fails or the
    argument is invalid.

  <Side Effects>
    None.

  <Returns>
    A base64-encoded string.
  """

    try:
        return binascii.b2a_base64(data).decode('utf-8').rstrip('=\n ')

    except (TypeError, binascii.Error) as e:
        raise sslib_exceptions.FormatError('Invalid base64'
                                           ' encoding: ' + str(e))
示例#13
0
def import_ed25519_publickey_from_file(filepath):
    """Imports custom JSON-formatted ed25519 public key from disk.

  NOTE: The signing scheme is set at key generation (see generate function).

  Arguments:
    filepath: The path to read the file from.

  Raises:
    FormatError: Argument is malformed.
    StorageError: Key file cannot be read.
    Error: Public key is malformed.

  Returns:
    An ed25519 public key object conformant with 'ED25519KEY_SCHEMA'.

  """
    formats.PATH_SCHEMA.check_match(filepath)

    # Load custom on-disk JSON formatted key and convert to its custom in-memory
    # dict key representation
    ed25519_key_metadata = util.load_json_file(filepath)
    ed25519_key, _ = keys.format_metadata_to_key(ed25519_key_metadata)

    # Check that the generic loading functions indeed loaded an ed25519 key
    if ed25519_key['keytype'] != 'ed25519':
        message = 'Invalid key type loaded: ' + repr(ed25519_key['keytype'])
        raise exceptions.FormatError(message)

    return ed25519_key
示例#14
0
    def check_match(self, object):
        AnyString.check_match(self, object)

        if object == "":
            raise exceptions.FormatError(
                'Expected a string'
                ' with at least one character but got ' + repr(object))
示例#15
0
文件: formats.py 项目: sechkova/tuf
def expiry_string_to_datetime(expires):
    """
  <Purpose>
    Convert an expiry string to a datetime object.
  <Arguments>
    expires:
      The expiry date-time string in the ISO8601 format that is defined
      in securesystemslib.ISO8601_DATETIME_SCHEMA. E.g. '2038-01-19T03:14:08Z'
  <Exceptions>
    securesystemslib.exceptions.FormatError, if 'expires' cannot be
    parsed correctly.
  <Side Effects>
    None.
  <Returns>
    A datetime object representing the expiry time.
  """

    # Raise 'securesystemslib.exceptions.FormatError' if there is a mismatch.
    sslib_formats.ISO8601_DATETIME_SCHEMA.check_match(expires)

    try:
        return datetime.datetime.strptime(expires, "%Y-%m-%dT%H:%M:%SZ")
    except ValueError as error:
        raise sslib_exceptions.FormatError('Failed to parse ' + repr(expires) +
                                           ' as an expiry time') from error
示例#16
0
    def __init__(self, length):
        if isinstance(length, bool) or not isinstance(length, int):
            # We need to check for bool as a special case, since bool
            # is for historical reasons a subtype of int.
            raise exceptions.FormatError('Got ' + repr(length) +
                                         ' instead of an integer.')

        self._bytes_length = length
示例#17
0
 def check_match(self, object):
     # Simply return as soon as we find a match.
     # Raise 'exceptions.FormatError' if no matches are found.
     for alternative in self._alternatives:
         if alternative.matches(object):
             return
     raise exceptions.FormatError('Object did not match a'
                                  ' recognized alternative.')
示例#18
0
    def check_match(self, object):
        if not isinstance(object, dict):
            raise exceptions.FormatError('Expected a dict but'
                                         ' got ' + repr(object))

        for key, value in six.iteritems(object):
            self._key_schema.check_match(key)
            self._value_schema.check_match(value)
示例#19
0
def import_privatekey_from_file(filepath,
                                key_type=None,
                                password=None,
                                prompt=False):
    """Imports private key from file.

  If a password is passed or entered on the prompt, the private key is
  decrypted, otherwise it is treated as unencrypted.

  NOTE: The default signing scheme 'rsassa-pss-sha256' is assigned to RSA keys.
  Use 'import_rsa_privatekey_from_file' to specify any other than the default
  signing scheme for an RSA key. ed25519 and ecdsa keys have the signing scheme
  included in the custom key format (see generate functions).

  Arguments:
    filepath: The path to read the file from.
    key_type (optional): One of KEY_TYPE_RSA, KEY_TYPE_ED25519 or
        KEY_TYPE_ECDSA. Default is KEY_TYPE_RSA.
    password (optional): A password to decrypt the key.
    prompt (optional): A boolean indicating if the user should be prompted
        for a decryption password. If the user enters an empty password, the
        key is not decrypted.

  Raises:
    FormatError: Arguments are malformed or 'key_type' is not supported.
    ValueError: Both a 'password' is passed and 'prompt' is true.
    UnsupportedLibraryError: pyca/cryptography is not available.
    StorageError: Key file cannot be read.
    Error, CryptoError: Key cannot be parsed.

  Returns:
    A private key object conformant with one of 'ED25519KEY_SCHEMA',
    'RSAKEY_SCHEMA' or 'ECDSAKEY_SCHEMA'.

  """
    if key_type is None:
        key_type = KEY_TYPE_RSA

    if key_type == KEY_TYPE_ED25519:
        return import_ed25519_privatekey_from_file(filepath,
                                                   password=password,
                                                   prompt=prompt)

    elif key_type == KEY_TYPE_RSA:
        return import_rsa_privatekey_from_file(filepath,
                                               password=password,
                                               prompt=prompt)

    elif key_type == KEY_TYPE_ECDSA:
        return import_ecdsa_privatekey_from_file(filepath,
                                                 password=password,
                                                 prompt=prompt)

    else:
        raise exceptions.FormatError(
            "Unsupported key type '{}'. Must be '{}', '{}' or '{}'.".format(
                key_type, KEY_TYPE_RSA, KEY_TYPE_ED25519, KEY_TYPE_ECDSA))
示例#20
0
    def __init__(self, key_schema, value_schema):
        """
    <Purpose>
      Create a new DictOf schema.

    <Arguments>
      key_schema:  The dictionary's key.
      value_schema: The dictionary's value.
    """

        if not isinstance(key_schema, Schema):
            raise exceptions.FormatError('Expected Schema but'
                                         ' got ' + repr(key_schema))

        if not isinstance(value_schema, Schema):
            raise exceptions.FormatError('Expected Schema but'
                                         ' got ' + repr(value_schema))

        self._key_schema = key_schema
        self._value_schema = value_schema
示例#21
0
    def check_match(self, object):
        if not isinstance(object, (list, tuple)):
            raise exceptions.FormatError(
                'Expected object of type {} but got type {}'.format(
                    self._list_name,
                    type(object).__name__))

        # Check if all the items in the 'object' list
        # match 'schema'.
        for item in object:
            try:
                self._schema.check_match(item)

            except exceptions.FormatError as e:
                raise exceptions.FormatError(
                    str(e) + ' in ' + repr(self._list_name))

        # Raise exception if the number of items in the list is
        # not within the expected range.
        if not (self._min_count <= len(object) <= self._max_count):
            raise exceptions.FormatError('Length of ' + repr(self._list_name) +
                                         ' out of range.')
示例#22
0
    def __init__(self,
                 pattern=None,
                 modifiers=0,
                 re_object=None,
                 re_name=None):
        """
    <Purpose>
      Create a new regular expression schema.

    <Arguments>
      pattern:  The pattern to match, or None if re_object is provided.
      modifiers:  Flags to use when compiling the pattern.
      re_object:  A compiled regular expression object.
      re_name: Identifier for the regular expression object.
    """

        if not isinstance(pattern, six.string_types):
            if pattern is not None:
                raise exceptions.FormatError(
                    repr(pattern) + ' is not a string.')

        if re_object is None:
            if pattern is None:
                raise exceptions.FormatError(
                    'Cannot compare against an unset regular expression')

            if not pattern.endswith('$'):
                pattern += '$'
            re_object = re.compile(pattern, modifiers)
        self._re_object = re_object

        if re_name is None:
            if pattern is not None:
                re_name = 'pattern /' + pattern + '/'

            else:
                re_name = 'pattern'
        self._re_name = re_name
示例#23
0
    def __init__(self, object_name='object', **required):
        """
    <Purpose>
      Create a new Object schema.

    <Arguments>
      object_name: A string identifier for the object argument.

      A variable number of keyword arguments is accepted.
    """

        # Ensure valid arguments.
        for key, schema in six.iteritems(required):
            if not isinstance(schema, Schema):
                raise exceptions.FormatError('Expected Schema but'
                                             ' got ' + repr(schema))

        self._object_name = object_name
        self._required = list(required.items())
示例#24
0
def _encode_canonical(object, output_function):
    # Helper for encode_canonical.  Older versions of json.encoder don't
    # even let us replace the separators.

    if isinstance(object, str):
        output_function(_canonical_string_encoder(object))
    elif object is True:
        output_function("true")
    elif object is False:
        output_function("false")
    elif object is None:
        output_function("null")
    elif isinstance(object, int):
        output_function(str(object))
    elif isinstance(object, (tuple, list)):
        output_function("[")
        if len(object):
            for item in object[:-1]:
                _encode_canonical(item, output_function)
                output_function(",")
            _encode_canonical(object[-1], output_function)
        output_function("]")
    elif isinstance(object, dict):
        output_function("{")
        if len(object):
            items = sorted(object.items())
            for key, value in items[:-1]:
                output_function(_canonical_string_encoder(key))
                output_function(":")
                _encode_canonical(value, output_function)
                output_function(",")
            key, value = items[-1]
            output_function(_canonical_string_encoder(key))
            output_function(":")
            _encode_canonical(value, output_function)
        output_function("}")
    else:
        raise exceptions.FormatError('I cannot encode ' + repr(object))
示例#25
0
文件: formats.py 项目: sechkova/tuf
def datetime_to_unix_timestamp(datetime_object):
    """
  <Purpose>
    Convert 'datetime_object' (in datetime.datetime()) format) to a Unix/POSIX
    timestamp.  For example, Python's time.time() returns a Unix timestamp, and
    includes the number of microseconds.  'datetime_object' is converted to UTC.

    >>> datetime_object = datetime.datetime(1985, 10, 26, 1, 22)
    >>> timestamp = datetime_to_unix_timestamp(datetime_object)
    >>> timestamp
    499137720

  <Arguments>
    datetime_object:
      The datetime.datetime() object to convert to a Unix timestamp.

  <Exceptions>
    securesystemslib.exceptions.FormatError, if 'datetime_object' is not a
    datetime.datetime() object.

  <Side Effects>
    None.

  <Returns>
    A unix (posix) timestamp (e.g., 499137660).
  """

    # Is 'datetime_object' a datetime.datetime() object?
    # Raise 'securesystemslib.exceptions.FormatError' if not.
    if not isinstance(datetime_object, datetime.datetime):
        message = repr(
            datetime_object) + ' is not a datetime.datetime() object.'
        raise sslib_exceptions.FormatError(message)

    unix_timestamp = calendar.timegm(datetime_object.timetuple())

    return unix_timestamp
示例#26
0
    def __init__(self,
                 schema,
                 min_count=0,
                 max_count=sys.maxsize,
                 list_name='list'):
        """
    <Purpose>
      Create a new ListOf schema.

    <Arguments>
      schema:  The pattern to match.
      min_count: The minimum number of sub-schema in 'schema'.
      max_count: The maximum number of sub-schema in 'schema'.
      list_name: A string identifier for the ListOf object.
    """

        if not isinstance(schema, Schema):
            message = 'Expected Schema type but got ' + repr(schema)
            raise exceptions.FormatError(message)

        self._schema = schema
        self._min_count = min_count
        self._max_count = max_count
        self._list_name = list_name
示例#27
0
 def check_match(self, object):
     if not isinstance(object, bool):
         raise exceptions.FormatError('Got ' + repr(object) +
                                      ' instead of a boolean.')
示例#28
0
def encode_canonical(object, output_function=None):
    """
  <Purpose>
    Encode 'object' in canonical JSON form, as specified at
    http://wiki.laptop.org/go/Canonical_JSON .  It's a restricted
    dialect of JSON in which keys are always lexically sorted,
    there is no whitespace, floats aren't allowed, and only quote
    and backslash get escaped.  The result is encoded in UTF-8,
    and the resulting bits are passed to output_function (if provided),
    or joined into a string and returned.

    Note: This function should be called prior to computing the hash or
    signature of a JSON object in securesystemslib.  For example, generating a
    signature of a signing role object such as 'ROOT_SCHEMA' is required to
    ensure repeatable hashes are generated across different json module
    versions and platforms.  Code elsewhere is free to dump JSON objects in any
    format they wish (e.g., utilizing indentation and single quotes around
    object keys).  These objects are only required to be in "canonical JSON"
    format when their hashes or signatures are needed.

    >>> encode_canonical("")
    '""'
    >>> encode_canonical([1, 2, 3])
    '[1,2,3]'
    >>> encode_canonical([])
    '[]'
    >>> encode_canonical({"A": [99]})
    '{"A":[99]}'
    >>> encode_canonical({"x" : 3, "y" : 2})
    '{"x":3,"y":2}'

  <Arguments>
    object:
      The object to be encoded.

    output_function:
      The result will be passed as arguments to 'output_function'
      (e.g., output_function('result')).

  <Exceptions>
    securesystemslib.exceptions.FormatError, if 'object' cannot be encoded or
    'output_function' is not callable.

  <Side Effects>
    The results are fed to 'output_function()' if 'output_function' is set.

  <Returns>
    A string representing the 'object' encoded in canonical JSON form.
  """

    result = None
    # If 'output_function' is unset, treat it as
    # appending to a list.
    if output_function is None:
        result = []
        output_function = result.append

    try:
        _encode_canonical(object, output_function)

    except (TypeError, exceptions.FormatError) as e:
        message = 'Could not encode ' + repr(object) + ': ' + str(e)
        raise exceptions.FormatError(message)

    # Return the encoded 'object' as a string.
    # Note: Implies 'output_function' is None,
    # otherwise results are sent to 'output_function'.
    if result is not None:
        return ''.join(result)
示例#29
0
 def check_match(self, object):
     if not isinstance(
             object, six.string_types) or not self._re_object.match(object):
         raise exceptions.FormatError(
             repr(object) + ' did not match ' + repr(self._re_name))
示例#30
0
 def __init__(self, schema):
     if not isinstance(schema, Schema):
         raise exceptions.FormatError('Expected Schema, but'
                                      ' got ' + repr(schema))
     self._schema = schema