Пример #1
0
 def __init__(self, conf, directory, destination=None, intermediate=None,
                                                       suffix="cpp",
                                                       prefix="yodogg",
                                                       use_cdb=True,
                                                       do_shared=True, do_static=True,
                                                       do_preload=True,
                                                     **kwargs):
     if not conf:
         raise CompilerError("A config-ish instance is required")
     if not suffix:
         suffix = "cpp"
     if not prefix:
         prefix = "yodogg"
     self.MAXIMUM =  int(kwargs.pop('maximum', DEFAULT_MAXIMUM_GENERATOR_COUNT))
     self.VERBOSE = bool(kwargs.pop('verbose', DEFAULT_VERBOSITY))
     self.conf = conf
     self.prefix = u8str(prefix)
     self.suffix = u8str(suffix).lower()
     self.do_shared = bool(do_shared)
     self.do_static = bool(do_static)
     self.do_preload = bool(do_shared) and bool(do_preload)
     self.use_cdb = bool(use_cdb)
     self.directory = Directory(pth=directory)
     if not self.directory.exists:
         raise CompilerError(f"Non-existant generator source directory: {self.directory}")
     self.destination = Directory(pth=destination)
     if not self.destination.exists:
         self.destination.makedirs()
     self.library = self.destination.subpath(f"{self.prefix}{SHARED_LIBRARY_SUFFIX}")
     self.archive = self.destination.subpath(f"{self.prefix}{STATIC_LIBRARY_SUFFIX}")
     self.intermediate = Intermediate(pth=intermediate)
     if not self.intermediate.exists:
         self.intermediate.makedirs()
     cdb = kwargs.pop('cdb', None)
     self.cdb = self.use_cdb and (cdb or CDBJsonFile(directory=self.intermediate)) or None
     self._precompiled = False
     self._compiled = False
     self._postcompiled = False
     self._linked = False
     self._archived = False
     self._preloaded = False
     self.sources = OCDList()
     self.prelink = OCDList()
     self.link_result = tuple()
     self.archive_result = tuple()
     self.preload_result = None
     if self.VERBOSE:
         print("")
         print("Initialized Halide generator compile/load/run suite:")
         print(f"* Config class: {self.conf.name}")
         print(f"* Using source: {self.directory}")
         print(f"* With targets: {self.destination}")
         if do_shared:
             print(f"*      Library: {self.library}")
         if do_static:
             print(f"*      Archive: {self.archive}")
         if use_cdb:
             print(f"*   Compile DB: {repr(self.cdb)}")
         print(f"* Intermediate: {self.intermediate}")
         print("")
Пример #2
0
    def sign(self, message, pvt_key):
        """
        Generate a cryptographic signature for the supplied message using a LMOTS private key.
        Algorithm 3: Generating a Signature from a Private Key and a Message as defined in Hash-Based Signatures draft
        RFC.
        :param message: message bytes
        :param pvt_key: LMOTS private key object
        :return: LMOTS signature object
        """
        if not isinstance(pvt_key, LmotsPrivateKey):
            raise ValueError("pvt_key must be of type LmotsPrivateKey")

        if pvt_key.signatures_remaining != 1:
            raise ValueError("private key has no signature operations remaining")

        c = self._entropy_source.read(self.lmots_type.n)
        hash_q = digest(self.lmots_type.hash_alg, pvt_key.s + c + message + D_MESG)
        v = hash_q + Merkle.checksum(hash_q, self.lmots_type.w, self.lmots_type.ls)
        y = list()
        for i, x in enumerate(pvt_key.raw_key):
            tmp = x
            for j in xrange(0, Merkle.coef(v, i, self.lmots_type.w)):
                tmp = digest(self.lmots_type.hash_alg, pvt_key.s + tmp + u16str(i) + u8str(j) + D_ITER)
            y.append(tmp)
            pvt_key.signatures_remaining = 0
        lmots_sig = LmotsSignature(c, y, pvt_key.lmots_type)
        return LmotsSerializer.serialize_signature(lmots_sig)
Пример #3
0
 def push(self, source, command, directory=None, destination=None):
     if not source:
         raise CDBError("a file source is required per entry")
     entry = {
         'directory': os.fspath(directory or os.getcwd()),
         'command': u8str(command),
         'file': source
     }
     if destination:
         entry.update({'output': destination})
     self.entries[source] = entry
Пример #4
0
    def generate_public_key(self, s, pvt_key):
        """
        Generate LMOTS public key from a private key.  In most cases, a key-pair is generated by calling the
        generate_key_pair method.  Alternatively the public key can be derived from the private key at any time.
        Algorithm 1 as defined in Hash-Based Signatures draft RFC.
        :param s: entropy s value
        :param pvt_key: LMOTS private key object
        :return: LMOTS public key object
        """
        if not isinstance(pvt_key, LmotsPrivateKey):
            raise ValueError("pvt_key must be of type LmotsPrivateKey")

        outer_hash = create_digest(self.lmots_type.hash_alg)
        outer_hash.update(s)
        for i, pvt_key in enumerate(pvt_key.raw_key):
            tmp = pvt_key
            for j in xrange(0, 2 ** self.lmots_type.w - 1):
                tmp = digest(self.lmots_type.hash_alg, s + tmp + u16str(i) + u8str(j) + D_ITER)
            outer_hash.update(tmp)
        outer_hash.update(D_PBLC)

        return LmotsPublicKey(s=s, k=outer_hash.digest(), lmots_type=self.lmots_type)
Пример #5
0
    def extract_public_key(self, signature, s, message):
        """
        Extracts a LMOTS public key object from a LMOTS signature and the s value.
        :param signature: LMOTS signature
        :param s: entropy s value
        :param message: original message
        :return: LMOTS public key object
        """
        lmots_sig = LmotsSerializer.deserialize_signature(signature)
        if lmots_sig.lmots_type != self.lmots_type:
            raise ValueError("signature type code does not match expected value")
        hash_q = digest(self.lmots_type.hash_alg, s + lmots_sig.c + message + D_MESG)
        v = hash_q + Merkle.checksum(hash_q, self.lmots_type.w, self.lmots_type.ls)
        outer_hash = create_digest(self.lmots_type.hash_alg)
        outer_hash.update(s)
        for i, y in enumerate(lmots_sig.y):
            tmp = y
            for j in xrange(Merkle.coef(v, i, self.lmots_type.w), 2 ** self.lmots_type.w - 1):
                tmp = digest(self.lmots_type.hash_alg, s + tmp + u16str(i) + u8str(j) + D_ITER)
            outer_hash.update(tmp)
        outer_hash.update(D_PBLC)

        return LmotsPublicKey(s=s, k=outer_hash.digest(), lmots_type=self.lmots_type)
Пример #6
0
 def run(self, target=None, emit=None, substitutions=None):
     """ Use the halogen.compile.Generators.run(…) method to run generators.
         
         All generator code that this instance knows about must have been previously compiled,
         dynamically linked, and preloaded. Assuming that all of these generators were properly
         programmed, they will then be available to halogen via the Halide Generator API --
         specifically the Generator Registry (q.v. `loaded_generators()` method docstring, supra).
     """
     # Check self-status:
     if not self.precompiled:
         raise GenerationError("Can’t run() before first precompiling, compiling, dynamic-linking, and preloading")
     if not self.compiled:
         raise GenerationError("Can’t run() before first compiling, dynamic-linking, and preloading")
     if not self.linked:
         raise GenerationError("Can’t run() before first dynamic-linking and preloading")
     if not self.preloaded:
         raise GenerationError("Can’t run() before first preloading")
     if self.loaded_count < 1:
         raise GenerationError("Can’t run() without one or more loaded generators")
     
     # Check args:
     if not target:
         target = 'host'
     
     if not substitutions:
         substitutions = {}
     
     emits = type(self).emits
     if not emit:
         emit = tuplize(*emits['default'])
     elif is_string(emit):
         emit = u8str(emit)
         if emit in emits:
             emit = tuplize(*emits.get(emit))
         else:
             possibles = ", ".join(OCDList(emits.keys()))
             raise GenerationError("String value for “emit” when calling Generators::run(…) "
                                  f"must be one of: {possibles}")
     else:
         emit = tuplize(*emit)
     
     if len(emit) < 1:
         possibles = ", ".join(emits['all'])
         raise GenerationError("Iterable value for “emit” when calling Generators::run(…) must contain "
                              f"one or more valid emit options (one of: {possibles})")
     
     # Run generators, storing output files in $TMP/yodogg
     artifacts = generate(*self.loaded_generators(), verbose=self.VERBOSE,
                                                     target=target,
                                                     emit=emit,
                                                     output_directory=self.destination,
                                                     substitutions=substitutions)
     
     # Re-dictify:
     generated = { artifact[2].name : dict(base_path=artifact[0],
                                           outputs=artifact[1],
                                           module=artifact[2]) for artifact in artifacts }
     
     # TELL ME ABOUT IT.
     if self.VERBOSE:
         module_names = ", ".join(u8str(key) for key in OCDList(generated.keys()))
         print(f"run(): Accreted {len(generated)} total generation artifacts")
         print(f"run(): Module names: {module_names}")
     
     # Return redictified artifacts:
     return generated
Пример #7
0
 def __str__(self):
     return u8str(json.dumps(self.rollout()))
Пример #8
0
def generate(*generators, **arguments):
    """ Invoke halogen.api.Module.compile(…) with the proper arguments. This function
        was concieved with replacing GenGen.cpp’s options in mind. """
    import os
    if __package__ is None or __package__ == '':
        import api # type: ignore
        from config import DEFAULT_VERBOSITY
        from errors import GenerationError
        from filesystem import Directory
        from utils import terminal_width, u8bytes, u8str
    else:
        from . import api # type: ignore
        from .config import DEFAULT_VERBOSITY
        from .errors import GenerationError
        from .filesystem import Directory
        from .utils import terminal_width, u8bytes, u8str
    
    # ARGUMENT PROCESSING:
    
    generators = { u8str(generator) for generator in generators }
    generator_names = OCDFrozenSet(arguments.pop('generator_names', api.registered_generators()))
    output_directory = Directory(pth=arguments.pop('output_directory', None))
    target = api.Target(target_string=u8bytes(arguments.pop('target', 'host')))
    emits = OCDFrozenSet(arguments.pop('emit', default_emits))
    substitutions = dict(arguments.pop('substitutions', {}))
    verbose = bool(arguments.pop('verbose', DEFAULT_VERBOSITY))
    
    # ARGUMENT POST-PROCESS BOUNDS-CHECKS:
    
    if len(generators) == 0:
        raise GenerationError(">=1 generator is required")
    
    if len(generator_names) == 0:
        raise GenerationError(">=1 generator name is required")
    
    if not generators.issubset(generator_names):
        raise GenerationError(f"generator name in {str(generator_names)} unknown to set: {str(generators)}")
    
    if not output_directory.exists:
        output_directory.makedirs()
    
    if not emits.issubset(valid_emits):
        raise GenerationError(f"invalid emit in {str(emits)}")
    
    if verbose:
        print("")
        print(f"generate(): Preparing {len(generators)} generator modules to emit data …")
        print("")
    
    # Set what emits to, er, emit, as per the “emit” keyword argument;
    # These have been rolled into the “emits” set (q.v. argument processing supra.);
    # …plus, we’ve already ensured that the set is valid:
    emit_dict = dict(emit_defaults)
    for emit in emits:
        emit_dict[f"emit_{emit}"] = True
    
    # The “substitutions” keyword to the EmitOptions constructor is special;
    # It’s just a dict, passed forward during argument processing:
    emit_dict['substitutions'] = substitutions
    
    # Actually create the EmitOptions object from “emit_dict”:
    emit_options = api.EmitOptions(**emit_dict)
    
    if verbose:
        print(f"generate(): Target: {u8str(target)}")
        print("generate(): Emit Options:")
        print(u8str(emit_options))
        print("")
    
    # This list will store generator module compilation artifacts:
    artifacts = []
    
    if verbose:
        print('-' * max(terminal_width, 100))
    
    # The generator loop compiles each named generator:
    for generator in generators:
        
        # “base_path” (a bytestring) is computed using the `compute_base_path()` API function:
        base_path = api.compute_base_path(u8bytes(
                                        os.fspath(output_directory)),
                                          u8bytes(generator))
        
        # “output” (an instance of halogen.api.Outputs) is computed using the eponymously named
        # halogen.api.EmitOptions method `compute_outputs_for_target_and_path()` with an instance
        # of halogen.api.Target and a base path bytestring (q.v. note supra.):
        output = emit_options.compute_outputs_for_target_and_path(target, base_path)
        
        if verbose:
            print(f"BSEPTH: {u8str(base_path)}")
            print(f"OUTPUT: {u8str(output)}")
        
        # This API call prepares the generator code module:
        module = api.get_generator_module(generator,
                                          arguments={ 'target': target })
        
        if verbose:
            print(f"MODULE: {u8str(module.name)} ({u8str(module)})")
            print('=' * max(terminal_width, 100))
        
        # The module-compilation call:
        module.compile(output)
        
        # Stow the post-compile base path (a string), outputs (an instance of
        # halogen.api.Outputs) and the module instance itself:
        artifacts.append((u8str(base_path), output, module))
    
    # Return the post-compile value artifacts for all generators:
    return artifacts