def digest(blob, comment, bootargs, initramfs, kernel, seckey, password=None): with tempfile.NamedTemporaryFile() as temp: esmd_create_digest(temp.name, bootargs, initramfs, kernel) blob_contents = file_read(temp.name) fdt = libfdt.Fdt(blob_contents) attach_digests(blob, temp.name, comment, 'digests-fdt', seckey, password)
def __init__(self, fname): self._fname = fname self._cached_offsets = False self.phandle_to_node = {} if self._fname: self._fname = fdt_util.EnsureCompiled(self._fname) with open(self._fname) as fd: self._fdt_obj = libfdt.Fdt(fd.read())
def __init__(self, fname): self._fname = fname self._cached_offsets = False if self._fname: self._fname = fdt_util.EnsureCompiled(self._fname) with open(self._fname) as fd: self._fdt = bytearray(fd.read()) self._fdt_obj = libfdt.Fdt(self._fdt)
def _ReadFdt(fname): """Read a device tree file into an Fdt object, ready for use Args: fname: Filename to read from Returns: Fdt bytearray suitable for passing to libfdt functions """ return libfdt.Fdt(open(fname).read())
def FromData(data): """Create a new Fdt object from the given data Args: data: Device-tree data blob Returns: Fdt object containing the data """ fdt = Fdt(None) fdt._fdt_obj = libfdt.Fdt(bytearray(data)) return fdt
def FromData(data, name=''): """Create a new Fdt object from the given data Args: data: Device-tree data blob name: Helpful name for this Fdt for the user Returns: Fdt object containing the data """ fdt = Fdt(None) fdt._fdt_obj = libfdt.Fdt(bytes(data)) fdt.name = name return fdt
def esm_detach(args): blob_contents = file_read(args.blob) fdt = libfdt.Fdt(blob_contents) validate_esm(fdt, args.blob) # Find and delete the attachment. file_path = '/file/' + args.file file_offset = fdt.path_offset(file_path, QUIET_NOTFOUND) if file_offset == -libfdt.NOTFOUND: err(1, '{}: not attached', args.file) dbg1('{}: deleting node', file_path) fdt.del_node(file_offset) # Write out the updated blob. fdt.pack() if not dryrun: file_atomic_replace(args.blob, fdt.as_bytearray())
def attach(blob, file, comment, name, seckey, password=None): blob_contents = file_read(blob) fdt = libfdt.Fdt(blob_contents) validate_esm(fdt, blob) file_fdt = file_fdt_get(fdt, create=True) # Load the attachment. file_contents = file_read(file) # Make room for the attachment, its comment, and other properties. file_fdt.resize(file_fdt.totalsize() + len(comment)) file_fdt.resize(file_fdt.totalsize() + len(file_contents) + EIGHT_KB) # Check that the attachment name is acceptable. if name == '': node_name = os.path.basename(file) else: node_name = name if not node_name_is_valid(node_name): err(1, '{}: file name is invalid: {}', file, node_name) # Create a subnode in /file for the attachment. parent_offset = file_fdt.path_offset('/files') file_offset = file_fdt.add_subnode(parent_offset, node_name) file_fdt.setprop_str(file_offset, 'algorithm', 'AES256-GCM') if '\n' in comment: err(1, 'comment cannot contain a newline') file_fdt.setprop_str(file_offset, 'untrusted-comment', comment) # Encrypt and store the attachment in the subnode. symkey = extract_symkey(fdt, seckey, password) aes_cipher = AES.new(symkey, AES.MODE_GCM) ciphertext, mac = aes_cipher.encrypt_and_digest(file_contents) file_fdt.setprop(file_offset, 'ciphertext', ciphertext) file_fdt.setprop(file_offset, 'iv', aes_cipher.nonce) file_fdt.setprop(file_offset, 'mac', mac) # Update files-fdt file_fdt_put(fdt, file_fdt) # Write out the updated blob. fdt.pack() if not dryrun: file_atomic_replace(blob, fdt.as_bytearray())
def esm_display(args): blob_contents = file_read(args.blob) fdt = libfdt.Fdt(blob_contents) validate_esm(fdt, args.blob) dbg1('reading lockboxes') # Find the offset to each lockbox and note its number. parent_offset = fdt.path_offset('/lockboxes') lockbox_offset = fdt.first_subnode(parent_offset) offset_by_number = dict() while lockbox_offset >= 0: lockbox_name = fdt.get_name(lockbox_offset) lockbox_num = -1 if lockbox_name == 'origin-lockbox': lockbox_num = 0 else: tokens = lockbox_name.split('-') lockbox_num = int(tokens[1]) offset_by_number[lockbox_num] = lockbox_offset lockbox_offset = fdt.next_subnode(lockbox_offset, QUIET_NOTFOUND) # Display the hash of the public key and the comment for each lockbox. # The origin key's hash is always displayed first. for num in sorted(offset_by_number): offset = offset_by_number[num] comment = fdt.getprop(offset, 'untrusted-comment').as_str() fingerprint_offset = fdt.subnode_offset(offset, 'pubkey-fingerprint') hash_str = fdt.getprop(fingerprint_offset, 'hash').hex() if num == 0: prefix = 'origin' else: prefix = 'recipient' print(prefix, hash_str, 'untrusted-comment', comment) dbg1('reading attachments') # Display the attachment and its comment parent_offset = fdt.path_offset('/file') file_offset = fdt.first_subnode(parent_offset, QUIET_NOTFOUND) while file_offset >= 0: comment = fdt.getprop(file_offset, 'untrusted-comment').as_str() name = fdt.get_name(file_offset) print('file', name, 'untrusted-comment', comment) file_offset = fdt.next_subnode(file_offset, QUIET_NOTFOUND)
def esm_revoke(args): header_contents = file_read(args.blob) fdt = libfdt.Fdt(header_contents) validate_esm(fdt, args.blob) # Prepare the pubkey hash. pubkey_contents = open(args.pubkey, 'rb').read() sha256 = SHA256.new() sha256.update(pubkey_contents) pubkey_hash = sha256.digest() dbg2('{}: SHA256: {}', args.pubkey, pubkey_hash.hex()) # Find a matching lockbox. parent_offset = fdt.path_offset('/lockboxes') offset = fdt.first_subnode(parent_offset) while offset > 0: lockbox_name = fdt.get_name(offset) fingerprint_path = '/lockboxes/' + lockbox_name + '/pubkey-fingerprint' fingerprint_offset = fdt.path_offset(fingerprint_path) lockbox_pubkey_hash = fdt.getprop(fingerprint_offset, 'hash') dbg2('{}: SHA256: {}', lockbox_name, lockbox_pubkey_hash.hex()) if pubkey_hash == lockbox_pubkey_hash: break offset = fdt.next_subnode(offset, libfdt.QUIET_NOTFOUND) # Not found. if offset <= 0: err(1, '{}: cannot revoke unauthorized key', args.pubkey) dbg1('{}: matching hash found', lockbox_name) # The origin key is a special case. We can never revoke it. if lockbox_name == "origin-lockbox": err(1, '{}: cannot revoke origin key', args.pubkey) # Remove the lockbox. dbg1('{}: deleting lockbox', lockbox_name) fdt.del_node(offset) # Write out the updated blob. fdt.pack() if not dryrun: file_atomic_replace(args.blob, fdt.as_bytearray())
def MicroblazeConfig(dtbfile, out): fdt = libfdt.Fdt(open(dtbfile, mode='rb').read()) cpu = -1 while (True): cpu = cpu + 1 try: node = fdt.path_offset('/cpus/cpu@%d' % cpu) try: prop = fdt.getprop(node, 'compatible') prop_val = prop[:-1].decode('utf-8').split('\x00') microblaze = False for val in prop_val: if "microblaze" in val: microblaze = True break if not microblaze: continue # Construct TUNE_FEATURE here TUNE_FEATURES = processProperties(fdt, node) out.write('AVAILTUNES += "microblaze-cpu%s"\n' % (cpu)) out.write('TUNE_FEATURES_tune-microblaze-cpu%s = "%s"\n' % (cpu, ' '.join(TUNE_FEATURES))) out.write( 'PACKAGE_EXTRA_ARCHS_tune-microblaze-cpu%s = "${TUNE_PKGARCH}"\n' % (cpu)) except Exception as e: sys.stderr.write("Exception looking at properties: %s\n" % e) continue except Exception as e: # CPUs SHOULD be consecutive w/o gaps, so no more to search break
def file_fdt_get(fdt, create=False): file_fdt = None file_path = '/file' file_offset = fdt.path_offset(file_path, QUIET_NOTFOUND) if file_offset == -libfdt.NOTFOUND: err(1, f'{file_path} not found') files_prop = fdt.getprop(file_offset, 'files-fdt', QUIET_NOTFOUND) if files_prop == -libfdt.NOTFOUND: # New File FDT with a "compatible" property. file_fdt = libfdt.Fdt.create_empty_tree(EIGHT_KB) file_fdt.setprop_str(0, 'compatible', 'ibm,esm-file') file_fdt.add_subnode(0, 'files') file_fdt.pack() fdt.resize(file_fdt.totalsize() + EIGHT_KB) fdt.setprop(file_offset, 'files-fdt', bytes(file_fdt.as_bytearray())) else: file_fdt = libfdt.Fdt(files_prop) return file_fdt
def esm_extract(args): blob_contents = file_read(args.blob) fdt = libfdt.Fdt(blob_contents) validate_esm(fdt, args.blob) # Find the attachment. file_path = '/file/' + args.file file_offset = fdt.path_offset(file_path, QUIET_NOTFOUND) if file_offset == -libfdt.NOTFOUND: err(1, '{}: not attached', args.file) # We only do AES256-GCM. algorithm = fdt.getprop(file_offset, 'algorithm').as_str() if algorithm != 'AES256-GCM': err(1, 'unsupported algorithm: {}', algorithm) # Grab the needed properties. ciphertext = fdt.getprop(file_offset, 'ciphertext') iv = fdt.getprop(file_offset, 'iv') mac = fdt.getprop(file_offset, 'mac') # Prepare our symmetric cipher. symkey = extract_symkey(fdt, args.seckey) aes_cipher = AES.new(symkey, AES.MODE_GCM, nonce=iv) # Decrypt and verify everything. plaintext = aes_cipher.decrypt(ciphertext) try: aes_cipher.verify(mac) except ValueError as e: err(1, '{}: cannot decrypt: wrong key or attachment corrupted', args.file) # Write the attached file's contents to the standard output. if not dryrun: print(plaintext.decode('ASCII'), end='')
def authorize(blob, pubkey, comment, seckey, password=None): blob_contents = file_read(blob) fdt = libfdt.Fdt(blob_contents) validate_esm(fdt, blob) # Prepare the pubkey hash. pubkey_contents = file_read(pubkey) sha256 = SHA256.new() sha256.update(pubkey_contents) pubkey_hash = sha256.digest() dbg2('{}: SHA256: {}', pubkey, pubkey_hash.hex()) # Find the largest lockbox number. parent_offset = fdt.path_offset('/lockboxes') offset = fdt.first_subnode(parent_offset) max_lockbox_num = -1 while offset > 0: lockbox_name = fdt.get_name(offset) fingerprint_offset = fdt.subnode_offset(offset, 'pubkey-fingerprint') lockbox_pubkey_hash = fdt.getprop(fingerprint_offset, 'hash') dbg2('{}: SHA256: {}', lockbox_name, lockbox_pubkey_hash.hex()) if pubkey_hash == lockbox_pubkey_hash: break lockbox_num = -1 if lockbox_name == 'origin-lockbox': lockbox_num = 0 else: tokens = lockbox_name.split('-') lockbox_num = int(tokens[1]) max_lockbox_num = max(max_lockbox_num, lockbox_num) offset = fdt.next_subnode(offset, libfdt.QUIET_NOTFOUND) # If the key is already authorized there's nothing more to do. if offset > 0: err(1, '{}: already authorized', pubkey) # Need more room for new lockbox. fdt.resize(fdt.totalsize() + len(comment) + EIGHT_KB) # Create a new lockbox and add the comment, if any. lockbox_name = "lockbox-{}".format(max_lockbox_num + 1) dbg1('{}: making new lockbox', lockbox_name) lockbox_offset = fdt.add_subnode(parent_offset, lockbox_name) if '\n' in comment: err(1, 'comment cannot contain a newline') fdt.setprop_str(lockbox_offset, "untrusted-comment", comment) # Add the pubkey fingerprint. fingerprint_offset = fdt.add_subnode(lockbox_offset, 'pubkey-fingerprint') fdt.setprop_str(fingerprint_offset, 'algorithm', 'SHA256') fdt.setprop(fingerprint_offset, 'hash', pubkey_hash) # Prepare the public key cipher. try: rsa_pubkey = RSA.importKey(pubkey_contents) except ValueError as e: err(1, '{}: {}', pubkey, *e.args) rsa_cipher = PKCS1_OAEP.new(rsa_pubkey, hashAlgo=SHA256) # Add the encrypted symkey. symkey = extract_symkey(fdt, seckey, password) encrypted_symkey = rsa_cipher.encrypt(symkey) fdt.setprop(lockbox_offset, 'encrypted-symkey', encrypted_symkey) # Write out the updated blob. fdt.pack() if not dryrun: file_atomic_replace(blob, fdt.as_bytearray())
def testBadFdt(self): """Check that a filename provided accidentally is not accepted""" with self.assertRaises(FdtException) as e: fdt = libfdt.Fdt('a string') self.assertEquals(e.exception.err, -libfdt.BADMAGIC)