Exemplo n.º 1
0
  def __init__(self, sub_schemas, optional_schemas=[], allow_more=False,
               struct_name='list'):
    """
    <Purpose> 
      Create a new Struct schema.

    <Arguments>
      sub_schemas: The sub-schemas recognized.
      optional_schemas: The optional list of schemas.
      allow_more: Specifies that an optional list of types is allowed.
      struct_name: A string identifier for the Struct object.
    """
    
    # Ensure each item of the list contains the expected object type.
    if not isinstance(sub_schemas, (list, tuple)):
      message = 'Expected Schema but got ' + repr(sub_schemas)
      raise tuf.FormatError(message)
    for schema in sub_schemas:
      if not isinstance(schema, Schema):
        raise tuf.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
Exemplo n.º 2
0
  def check_match(self, object):
    if not isinstance(object, six.binary_type):
      raise tuf.FormatError('Expected a byte but got ' + repr(object))

    if len(object) != self._bytes_length:
      raise tuf.FormatError('Expected a byte of length ' + \
                            repr(self._bytes_length))
Exemplo n.º 3
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 tuf.FormatError(repr(pattern) + ' is not a string.')
        
    if re_object is None:
      if pattern is None:
        error = 'Cannot compare against an unset regular expression'
        raise tuf.FormatError(error)
      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
Exemplo n.º 4
0
  def check_match(self, object):
    if not isinstance(object, six.string_types):
      raise tuf.FormatError('Expected a string but got ' + repr(object))

    if len(object) != self._string_length:
      raise tuf.FormatError('Expected a string of length ' + \
                            repr(self._string_length))
Exemplo n.º 5
0
    def check_match(self, object):
        if not isinstance(object, dict):
            message = 'Wanted a ' + repr(self._object_name) + '.'
            raise tuf.FormatError(message)

        # (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):
                    message = 'Missing key ' + repr(key) + ' in ' + repr(
                        self._object_name)
                    raise tuf.FormatError(message)
            # Check that 'object's schema matches Object()'s schema for this
            # particular 'key'.
            else:
                try:
                    schema.check_match(item)
                except tuf.FormatError, e:
                    raise tuf.FormatError(
                        str(e) + ' in ' + self._object_name + '.' + key)
Exemplo n.º 6
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>
    tuf.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, basestring):
        message = 'Invalid argument: ' + repr(base64_string)
        raise tuf.FormatError(message)

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

    try:
        return binascii.a2b_base64(base64_string)
    except (TypeError, binascii.Error), e:
        raise tuf.FormatError('Invalid base64 encoding: ' + str(e))
Exemplo n.º 7
0
 def __init__(self, required_schemas):
   # Ensure each item of the list contains the expected object type.
   if not isinstance(required_schemas, list):
     raise tuf.FormatError('Expected a list but got'+repr(required_schemas))
   for schema in required_schemas:
     if not isinstance(schema, Schema):
       raise tuf.FormatError('List contains an invalid item '+repr(schema))
   
   self._required_schemas = required_schemas[:]
Exemplo n.º 8
0
 def __init__(self, alternatives):
   # Ensure each item of the list contains the expected object type.
   if not isinstance(alternatives, list):
     raise tuf.FormatError('Expected a list but got ' + repr(alternatives))
   for alternative in alternatives:
     if not isinstance(alternative, Schema):
       raise tuf.FormatError('List contains an invalid item ' + repr(alternative))
   
   self._alternatives = alternatives
Exemplo n.º 9
0
 def check_match(self, object):
   if isinstance(object, bool) or not isinstance(object, six.integer_types):
     # We need to check for bool as a special case, since bool
     # is for historical reasons a subtype of int.
     raise tuf.FormatError('Got '+repr(object)+' instead of an integer.')
   
   elif not (self._lo <= object <= self._hi):
     int_range = '['+repr(self._lo)+', '+repr(self._hi)+'].'
     raise tuf.FormatError(repr(object)+' not in range '+int_range)
Exemplo n.º 10
0
  def check_match(self, object):
    if not isinstance(object, (list, tuple)):
      message = 'Expected '+repr(self._list_name)+' but got '+repr(object)
      raise tuf.FormatError(message)

    # Check if all the items in the 'object' list
    # match 'schema'.
    for item in object:
      try:
        self._schema.check_match(item)
      except tuf.FormatError, e:
        raise tuf.FormatError(str(e)+' in '+repr(self._list_name))
Exemplo n.º 11
0
def check_signable_object_format(object):
    """
  <Purpose>
    Ensure 'object' is properly formatted, conformant to
    'tuf.formats.SIGNABLE_SCHEMA'.  Return the signing role on success.
    Note: The 'signed' field of a 'SIGNABLE_SCHEMA' is checked against
    tuf.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>
    object:
     The object compare against 'SIGNABLE.SCHEMA'. 

  <Exceptions>
    tuf.FormatError, if 'object' does not have the correct format.

  <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 'object' have the correct type?
    # This check ensures 'object' conforms to
    # 'tuf.formats.SIGNABLE_SCHEMA'.
    SIGNABLE_SCHEMA.check_match(object)

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

    except (KeyError, TypeError):
        raise tuf.FormatError('Untyped object')

    try:
        schema = SCHEMAS_BY_TYPE[role_type]

    except KeyError:
        raise tuf.FormatError('Unrecognized type ' + repr(role_type))

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

    return role_type.lower()
Exemplo n.º 12
0
def parse_time(string):
    """
  <Purpose>
    Parse 'string', in 'YYYY-MM-DD HH:MM:SS UTC' format, to a Unix timestamp.

  <Arguments>
    string:
      A string representing the time (e.g., '1985-10-26 01:20:00 UTC').

  <Exceptions>
    tuf.FormatError, if parsing 'string' fails.

  <Side Effects>
    None.

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

  """

    # Is 'string' properly formatted?
    # Raise 'tuf.FormatError' if there is a mismatch.
    TIME_SCHEMA.check_match(string)

    # Strip the ' UTC' attached to the string.  The string time, minus the ' UTC',
    # is the time format expected by the time functions called below.
    string = string[0:string.rfind(' UTC')]
    try:
        return calendar.timegm(time.strptime(string, '%Y-%m-%d %H:%M:%S'))
    except ValueError:
        raise tuf.FormatError('Malformed time: ' + repr(string))
Exemplo n.º 13
0
    def __init__(self,
                 schema,
                 min_count=0,
                 max_count=sys.maxint,
                 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 tuf.FormatError(message)

        self._schema = schema
        self._min_count = min_count
        self._max_count = max_count
        self._list_name = list_name
Exemplo n.º 14
0
def format_base64(data):
    """
  <Purpose>
    Return the base64 encoding of 'data' with whitespace
    and '=' signs omitted.

  <Arguments>
    data:
      A string or buffer of data to convert.

  <Exceptions>
    tuf.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).rstrip('=\n ')
    except (TypeError, binascii.Error), e:
        raise tuf.FormatError('Invalid base64 encoding: ' + str(e))
Exemplo n.º 15
0
 def __init__(self, length):
   if isinstance(length, bool) or not isinstance(length, six.integer_types):
     # We need to check for bool as a special case, since bool
     # is for historical reasons a subtype of int.
     raise tuf.FormatError('Got ' + repr(length) + ' instead of an integer.')
   
   self._bytes_length = length 
Exemplo n.º 16
0
 def check_match(self, object):
   # Simply return as soon as we find a match.
   # Raise 'tuf.FormatError' if no matches are found.
   for alternative in self._alternatives:
     if alternative.matches(object):
       return
   raise tuf.FormatError('Object did not match a recognized alternative.')
Exemplo n.º 17
0
  def check_match(self, object):
    if not isinstance(object, dict): 
      raise tuf.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)
Exemplo n.º 18
0
def format_time(timestamp):
    """
  <Purpose>
    Encode 'timestamp' in 'YYYY-MM-DD HH:MM:SS UTC' format.
    'timestamp' is a Unix timestamp value.  For example, it is the time
    format returned by calendar.timegm(). 

    >>> format_time(499137720)
    '1985-10-26 01:22:00 UTC'

  <Arguments>
    timestamp:
      The time to format.  This is a Unix timestamp.

  <Exceptions>
    tuf.Error, if 'timestamp' is invalid.

  <Side Effects>
    None.

  <Returns>
    A string in 'YYYY-MM-DD HH:MM:SS UTC' format.

  """

    try:
        # Convert the timestamp to 'yyyy-mm-dd HH:MM:SS' format.
        formatted_time = time.strftime('%Y-%m-%d %H:%M:%S',
                                       time.gmtime(timestamp))

        # Attach 'UTC' to the formatted time string prior to returning.
        return formatted_time + ' UTC'
    except (ValueError, TypeError):
        raise tuf.FormatError('Invalid argument value')
Exemplo n.º 19
0
def safe_download(url, required_length):
  """
  <Purpose>
    Given the 'url' and 'required_length' of the desired file, open a connection
    to 'url', download it, and return the contents of the file.  Also ensure
    the length of the downloaded file matches 'required_length' exactly.
    tuf.download.unsafe_download() may be called if an upper download limit is
    preferred.
 
    'tuf.util.TempFile', the file-like object returned, is used instead of
    regular tempfile object because of additional functionality provided, such
    as handling compressed metadata and automatically closing files after
    moving to final destination.
  
  <Arguments>
    url:
      A URL string that represents the location of the file.  The URI scheme
      component must be one of 'tuf.conf.SUPPORTED_URI_SCHEMES'.
  
    required_length:
      An integer value representing the length of the file.  This is an exact
      limit.

  <Side Effects>
    A 'tuf.util.TempFile' object is created on disk to store the contents of
    'url'.
 
  <Exceptions>
    tuf.DownloadLengthMismatchError, if there was a mismatch of observed vs
    expected lengths while downloading the file.
 
    tuf.FormatError, if any of the arguments are improperly formatted.

    Any other unforeseen runtime exception.
 
  <Returns>
    A 'tuf.util.TempFile' file-like object that points to the contents of 'url'.
  """
  
  # Do all of the arguments have the appropriate format?
  # Raise 'tuf.FormatError' if there is a mismatch.
  tuf.formats.URL_SCHEMA.check_match(url)
  tuf.formats.LENGTH_SCHEMA.check_match(required_length)

  # Ensure 'url' specifies one of the URI schemes in
  # 'tuf.conf.SUPPORTED_URI_SCHEMES'.  Be default, ['http', 'https'] is
  # supported.  If the URI scheme of 'url' is empty or "file", files on the
  # local system can be accessed.  Unexpected files may be accessed by
  # compromised metadata (unlikely to happen if targets.json metadata is signed
  # with offline keys).  
  parsed_url = six.moves.urllib.parse.urlparse(url)

  if parsed_url.scheme not in tuf.conf.SUPPORTED_URI_SCHEMES:
    message = \
      repr(url) + ' specifies an unsupported URI scheme.  Supported ' + \
      ' URI Schemes: ' + repr(tuf.conf.SUPPORTED_URI_SCHEMES)
    raise tuf.FormatError(message)
  
  return _download_file(url, required_length, STRICT_REQUIRED_LENGTH=True)
Exemplo n.º 20
0
  def check_match(self, object):
    if not isinstance(object, (list, tuple)):
      message = 'Expected '+repr(self._list_name)+' but got '+repr(object)
      raise tuf.FormatError(message)

    # Check if all the items in the 'object' list
    # match 'schema'.
    for item in object:
      try:
        self._schema.check_match(item)
      except tuf.FormatError as e:
        raise tuf.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 tuf.FormatError('Length of '+repr(self._list_name)+' out of range')
Exemplo n.º 21
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 tuf.FormatError('Expected Schema but got '+repr(key_schema))
   
    if not isinstance(value_schema, Schema):
      raise tuf.FormatError('Expected Schema but got '+repr(value_schema))
    
    self._key_schema = key_schema
    self._value_schema = value_schema
Exemplo n.º 22
0
 def check_match(self, object):
   if not isinstance(object, (list, tuple)):
     raise tuf.FormatError('Expected ' + repr(self._struct_name) + '; got ' + repr(object))
   elif len(object) < self._min:
     raise tuf.FormatError('Too few fields in ' + self._struct_name)
   elif len(object) > len(self._sub_schemas) and not self._allow_more:
     raise tuf.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
Exemplo n.º 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 tuf.FormatError('Expected Schema but got '+repr(schema))

    self._object_name = object_name
    self._required = list(required.items())
Exemplo n.º 24
0
def get_role_class(expected_rolename):
    """
  <Purpose>
    Return the role class corresponding to
    'expected_rolename'.  The role name returned
    by expected_meta_rolename() should be the name
    passed as an argument to this function.  If
    'expected_rolename' is 'Root', the class
    RootFile is returned.

  <Arguments>
    expected_rolename:
      The role name used to determine which role class
      to return.

  <Exceptions>
    tuf.FormatError, if 'expected_rolename' is not a
    supported role.

  <Side Effects>
    None.

  <Returns>
    The class corresponding to 'expected_rolename'.
    E.g., 'Snapshot' as an argument to this function causes
    SnapshotFile' to be returned. 
  """

    # Does 'expected_rolename' have the correct type?
    # This check ensures 'expected_rolename' conforms to
    # 'tuf.formats.NAME_SCHEMA'.
    # Raise 'tuf.FormatError' if there is a mismatch.
    NAME_SCHEMA.check_match(expected_rolename)

    try:
        role_class = ROLE_CLASSES_BY_TYPE[expected_rolename]

    except KeyError:
        raise tuf.FormatError(repr(expected_rolename) + ' not supported.')

    else:
        return role_class
Exemplo n.º 25
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, basestring):
        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, long)):
        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 = object.items()
            items.sort()
            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 tuf.FormatError('I cannot encode ' + repr(object))
Exemplo n.º 26
0
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>
    tuf.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 'tuf.FormatError' if not.
    if not isinstance(datetime_object, datetime.datetime):
        message = repr(
            datetime_object) + ' is not a datetime.datetime() object.'
        raise tuf.FormatError(message)

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

    return unix_timestamp
Exemplo n.º 27
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 pyca/cryptography library to
    perform the encryption and derive a suitable encryption key.

    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 = {'keytype': 'ed25519', \
                       'keyid': \
          'd62247f817883f593cf6c66a5a55292488d457bcf638ae03207dbbba9dbe457d', \
                       'keyval': {'public': \
          '74addb5ad544a4306b34741bc1175a3613a8d7dc69ff64724243efdec0e301ad', \
                                  'private': \
          '1f26964cc8d4f7ee5f3c5da2fbb7ab35811169573ac367b860a537e47789f8c4'}}
    >>> passphrase = 'secret'
    >>> encrypted_key = encrypt_key(ed25519_key, passphrase)
    >>> tuf.formats.ENCRYPTEDKEY_SCHEMA.matches(encrypted_key.encode('utf-8'))
    True

  <Arguments>
    key_object:
      The TUF key object that should contain the private portion of the ED25519
      key.

    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 any of the arguments are improperly formatted or
    'key_object' does not contain the private portion of the key.

    tuf.CryptoError, if an ED25519 key in encrypted TUF format cannot be
    created.

  <Side Effects>
    pyca/Cryptography cryptographic operations called to perform the actual
    encryption of 'key_object'.  'password' used to derive a suitable
    encryption key.

  <Returns>
    An encrypted string in 'tuf.formats.ENCRYPTEDKEY_SCHEMA' format.
  """

    I_TO_PRINT = TO_PRINT + uptane.YELLOW + '[encrypt_key(key_object, password)]: ' + uptane.ENDCOLORS
    #TODO: Print to be deleted
    print(
        str('%s %s %s %s %s' % (I_TO_PRINT, 'Encrypting key_object:',
                                key_object, 'with password:'******'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)

    # Ensure the private portion of the key is included in 'key_object'.
    if not key_object['keyval']['private']:
        raise tuf.FormatError('Key object does not contain a private part.')

    # Derive a key (i.e., an appropriate encryption key and not the
    # user's password) from the given 'password'.  Strengthen 'password' with
    # PBKDF2-HMAC-SHA256 (100K iterations by default, but may be overriden in
    # 'tuf.conf.PBKDF2_ITERATIONS' by the user).
    salt, iterations, derived_key = _generate_derived_key(password)

    # Store the derived key info in a dictionary, the object expected
    # by the non-public _encrypt() routine.
    derived_key_information = {
        'salt': salt,
        'iterations': iterations,
        'derived_key': derived_key
    }

    # Convert the key object to json string format and encrypt it with the
    # derived key.
    encrypted_key = _encrypt(json.dumps(key_object), derived_key_information)

    #TODO: Print to be deleted
    print(str('%s %s ' % (I_TO_PRINT, 'Returning encrypted_key')))
    #TODO: Until here

    return encrypted_key
Exemplo n.º 28
0
 def check_match(self, object):
     if self._string != object:
         message = 'Expected: ' + repr(self._string)
         raise tuf.FormatError(message)
Exemplo n.º 29
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 TUF.  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>
    tuf.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, e:
        message = 'Could not encode ' + repr(object) + ': ' + str(e)
        raise tuf.FormatError(message)
Exemplo n.º 30
0
def make_role_metadata(keyids,
                       threshold,
                       name=None,
                       paths=None,
                       path_hash_prefixes=None):
    """
  <Purpose>
    Create a dictionary conforming to 'tuf.formats.ROLE_SCHEMA',
    representing the role with 'keyids', 'threshold', and 'paths'
    as field values.  'paths' is optional (i.e., used only by the
    'Target' role).

  <Arguments>
    keyids: a list of key ids.

    threshold:
      An integer denoting the number of required keys
      for the signing role.

    name:
      A string that is the name of this role.

    paths:
      The 'Target' role stores the paths of target files
      in its metadata file.  'paths' is a list of
      file paths.

    path_hash_prefixes:
      The 'Target' role stores the paths of target files in its metadata file.
      'path_hash_prefixes' is a succint way to describe a set of paths to
      target files.

  <Exceptions>
    tuf.FormatError, if the returned role meta is
    formatted incorrectly.

  <Side Effects>
    If any of the arguments do not have a proper format, a 
    tuf.formats exception is raised when the 'ROLE_SCHEMA' dict
    is created.

  <Returns>
    A properly formatted role meta dict, conforming to
    'ROLE_SCHEMA'.
  """

    role_meta = {}
    role_meta['keyids'] = keyids
    role_meta['threshold'] = threshold

    if name is not None:
        role_meta['name'] = name

    # According to the specification, the 'paths' and 'path_hash_prefixes' must
    # be mutually exclusive. However, at the time of writing we do not always
    # ensure that this is the case with the schema checks (see #83). Therefore,
    # we must do it for ourselves.

    if paths is not None and path_hash_prefixes is not None:
        raise \
          tuf.FormatError('Both "paths" and "path_hash_prefixes" are specified.')

    if path_hash_prefixes is not None:
        role_meta['path_hash_prefixes'] = path_hash_prefixes
    elif paths is not None:
        role_meta['paths'] = paths

    # Does 'role_meta' have the correct type?
    # This check ensures 'role_meta' conforms to
    # tuf.formats.ROLE_SCHEMA.
    ROLE_SCHEMA.check_match(role_meta)

    return role_meta