Exemple #1
0
def create_signed_resource(options, manifestInput):

    # Create the Resource structure first, and encode it using specified encoding scheme
    # This encoded data will then be used for signing
    try:
        resource = Resource(resource=get_manifest(options, manifestInput),
                            resourceType=Resource.TYPE_MANIFEST)
    except errorhandler.InvalidObject as err:
        LOG.critical('Unable to create resource object: {0}'.format(err))
        sys.exit(1)

    # Convert the Python object to a Python dictionary, and then encode to
    # specified encoding format
    resource_encoded = utils.encode(resource.to_dict(), options,
                                    manifest_definition.Resource())

    signature = None
    crypto_mode = get_crypto_mode(options, manifestInput)
    if isPsk(crypto_mode):
        signature = get_symmetric_signature(options,
                                            manifestInput,
                                            enc_data=resource_encoded)
    else:
        signature = get_signature(options,
                                  manifestInput,
                                  enc_data=resource_encoded)
    try:
        sresource = SignedResource(resource=resource, signature=signature)
        return sresource.to_dict()
    except errorhandler.InvalidObject as err:
        LOG.critical('Unable to create signed resource: {0}'.format(err))
        sys.exit(1)
Exemple #2
0
def main(options):
    LOG.debug('Creating new manifest from input file and options')

    # Parse input files and options. Generate hydrated and hierachial manifest JSON.
    manifest = create_signed_resource(options)
    LOG.debug('Manifest python object successfully created from ASN.1 definition and input')

    # Encode data if requested
    output = utils.encode(manifest, options, manifest_definition.SignedResource())
    LOG.debug('Manifest successfully encoded into desired format ({}). Size: {} bytes.'.format(options.encoding, len(output)))

    # And we're done
    write_result(output, options)
Exemple #3
0
def create_signed_resource(options):
    # Read options from manifest input file/
    # if (options.input_file.isatty()):
    #     LOG.info("Reading data from from active TTY... Terminate input with ^D")
    manifestInput = {'applyImmediately': True}

    try:
        if os.path.exists(defaults.config):
            with open(defaults.config) as f:
                manifestInput.update(json.load(f))
        if not options.input_file.isatty():
            content = options.input_file.read()
            if content and len(
                    content) >= 2:  #The minimum size of a JSON file is 2: '{}'
                manifestInput.update(json.loads(content))
    except ValueError as e:
        LOG.critical("JSON Decode Error: {}".format(e))
        sys.exit(1)

    # Create the Resource structure first, and encode it using specified encoding scheme
    # This encoded data will then be used for signing
    try:
        resource = Resource(resource=get_manifest(options, manifestInput),
                            resourceType=Resource.TYPE_MANIFEST)
    except errorhandler.InvalidObject as err:
        LOG.critical('Unable to create resource object: {0}'.format(err))
        sys.exit(1)

    # Convert the Python object to a Python dictionary, and then encode to
    # specified encoding format
    resource_encoded = utils.encode(resource.to_dict(), options,
                                    manifest_definition.Resource())

    signature = None
    crypto_mode = get_crypto_mode(options, manifestInput)
    if isPsk(crypto_mode):
        signature = get_symmetric_signature(options,
                                            manifestInput,
                                            enc_data=resource_encoded)
    else:
        signature = get_signature(options,
                                  manifestInput,
                                  enc_data=resource_encoded)
    try:
        sresource = SignedResource(resource=resource, signature=signature)
        return sresource.to_dict()
    except errorhandler.InvalidObject as err:
        LOG.critical('Unable to create signed resource: {0}'.format(err))
        sys.exit(1)
Exemple #4
0
def sign(options):
    # Load defaults
    defaultConfig = {}
    if os.path.exists(defaults.config):
        with open(defaults.config) as f:
            defaultConfig = json.load(f)

    # Load the existing manifest
    content = None
    if hasattr(options, 'manifest') and options.manifest:
        options.manifest.seek(0)
        content = options.manifest.read()
    else:
        content = utils.download_file(options.url)

    # Extract the signed resource.
    signed_resource_data = {
        "der":
        lambda d: codec.bin2obj(d, definition.SignedResource(), der_decoder),
    }[options.encoding](content)
    if not signed_resource_data:
        return 1
    LOG.debug('Decoded SignedResource from {} encoded binary'.format(
        options.encoding))

    # Sign the resource
    # Calculate the hash of the content of the existing manifest
    c_hash = utils.sha_hash(signed_resource_data['resource'])
    if not 'hash' in signed_resource_data['signature']:
        LOG.critical('Manifest does not contain a hash')
        return None
    # Extract the hash
    e_hash = binascii.a2b_hex(signed_resource_data['signature']['hash'])
    # Compare calculated and extracted hashes
    if c_hash != e_hash:
        LOG.critical('Hash mismatch\nExpected: {}\nActual:   {}'.format(
            binascii.b2a_hex(e_hash), binascii.b2a_hex(c_hash)))
        return None
    LOG.debug('Manifest hash: {}'.format(binascii.b2a_hex(c_hash)))

    # Load a certificate and private key
    # Private key must be in .manifest_tool.json, or provided on the command-line

    # 1. Check the command-line
    if not hasattr(options, 'private_key') or not options.private_key:
        # 2. Check the default config
        if 'private-key' in defaultConfig:
            try:
                # NOTE: binary is not specified since the key is usually PEM encoded.
                options.private_key = open(defaultConfig['private-key'], 'r')
            except:
                LOG.critical(
                    'No private key specified and default key ({}) cannot be opened'
                    .format(defaultConfig['private-key']))
                return 1
    # 3. Fail if the private key is not found
    if not hasattr(options, 'private_key') or not options.private_key:
        LOG.critical(
            'No private key specified and default key ({}) cannot be opened'.
            format(defaultConfig['private-key']))
        return 1
    if not hasattr(options, 'password'):
        options.password = None

    # Load the private key
    privkey = load_pem_private_key(options.private_key.read(),
                                   password=options.password,
                                   backend=default_backend())

    # Make sure that this is an ECDSA key!
    if not isinstance(privkey, ec.EllipticCurvePrivateKey):
        LOG.critical('Private key was not an ECC private key')
        return 1
    LOG.debug('Loaded private key')
    LOG.info('Signing manifest...')
    # Create signature
    sig = privkey.sign(signed_resource_data['resource'],
                       ec.ECDSA(hashes.SHA256()))
    LOG.debug('Signature: {}'.format(binascii.b2a_hex(sig)))

    # destroy the privkey object
    privkey = None

    # Certificate must be in .manifest_tool.json, or provided on the command-line
    # Load the certificate
    if not hasattr(options, 'certificate') or not options.certificate:
        if 'default-certificates' in defaultConfig:
            options.certificate = open(
                defaultConfig['default-certificates'][0]['file'], 'rb')

    if not hasattr(options, 'certificate') or not options.certificate:
        LOG.critical(
            'No certificate specified and default certificate ({}) cannot be opened'
            .format(defaultConfig['default-certificates'][0]['file']))
        return 1

    # Load the certificate object from the DER file
    certObj = None
    try:
        certObj = x509.load_der_x509_certificate(options.certificate.read(),
                                                 default_backend())
    except ValueError as e:
        LOG.critical("X.509 Certificate Error in ({file}): {error}".format(
            error=e, file=options.certificate.name))
        return (1)

    if not certObj:
        LOG.critical("({file}) is not a valid certificate".format(file=cPath))
        return (1)

    # Make sure that the certificate is signed with SHA256
    if not isinstance(certObj.signature_hash_algorithm, hashes.SHA256):
        LOG.critical(
            "In ({file}): Only SHA256 certificates are supported by the update client at this time."
            .format(file=cPath))
        return (1)

    LOG.info('Verifying signature with supplied certificate...')

    # Verify the signature with the provided certificate to ensure that the update target will be able to do so
    try:
        pubkey = certObj.public_key()
        pubkey.verify(sig, signed_resource_data['resource'],
                      ec.ECDSA(hashes.SHA256()))
    except InvalidSignature as e:
        LOG.critical(
            'New signature failed to verify with supplied certificate ({})'.
            format(options.certificate.name))
        return 1

    # Store the fingerprint of the certificate
    fingerprint = certObj.fingerprint(hashes.SHA256())

    certificates = []
    # for idx in range(len())
    #     certObj = None
    #     try:
    #         certObj = x509.load_der_x509_certificate(options.certificate.read(), default_backend())
    #     except ValueError as e:
    #         LOG.critical("X.509 Certificate Error in ({file}): {error}".format(error=e, file=options.certificate.name))
    #         return(1)
    #
    #     if not certObj:
    #         LOG.critical("({file}) is not a valid certificate".format(file=cPath))
    #         return(1)
    #     if not isinstance(certObj.signature_hash_algorithm, hashes.SHA256):
    #         LOG.critical("In ({file}): Only SHA256 certificates are supported by the update client at this time.".format(file=cPath))
    #         return(1)
    #     LOG.debug('Creating certificate reference ({}) from {} with fingerprint {}'.format(idx, options.certificate.name, fingerprint))
    LOG.debug(
        'Creating certificate reference from {} with fingerprint {}'.format(
            options.certificate.name, fingerprint))
    uri = ''
    # TODO: Insert URI for delegation of trust
    cr = schema.CertificateReference(fingerprint=fingerprint, uri=uri)
    # Append the certificate reference to the current list
    # NOTE: Currently, only one certificate reference will exist in the certificates list, but with delegation of trust
    #       there will be more certificate references
    certificates.append(cr)
    LOG.debug(
        'Signed hash ({}) of encoded content ({} bytes) with resulting signature {}'
        .format(binascii.b2a_hex(c_hash), len(content), binascii.b2a_hex(sig)))

    signatures = []
    for s in signed_resource_data['signature']['signatures']:
        signatures.append(sig_from_dict(s))

    # encode the signature block
    signatures.append(
        schema.SignatureBlock(signature=sig, certificates=certificates))

    # encode the resource signature
    resource_signature = schema.ResourceSignature(hash=c_hash,
                                                  signatures=signatures)

    # encode the signed resource
    signed_resource = schema.SignedResource(
        resource=signed_resource_data['resource'],
        signature=resource_signature)

    # Convert the signed resource into a python dictionary
    manifest_dict = signed_resource.to_dict()
    # Encode the Python dictionary as a DER stream
    output = utils.encode(manifest_dict, options, definition.SignedResource())

    # Write the result to the output_file
    if hasattr(options, 'output_file') and options.output_file:
        # Write result to output file or stdout buffer.
        options.output_file.write(output)

        # Append newline if outputting to TTY
        if options.output_file.isatty():
            options.output_file.write(b'\n')
        return 0
    return 1