def _sign_fw(fw): # load the .cab file download_dir = app.config['DOWNLOAD_DIR'] fn = os.path.join(download_dir, fw.filename) try: with open(fn, 'rb') as f: cabarchive = CabArchive(f.read()) except IOError as e: raise NotImplementedError('cannot read %s: %s' % (fn, str(e))) # sign each component in the archive print('Signing: %s' % fn) for md in fw.mds: try: ploader.archive_sign(cabarchive, cabarchive[md.filename_contents]) except KeyError as _: raise NotImplementedError('no {} firmware found'.format(md.filename_contents)) # overwrite old file cab_data = cabarchive.save() with open(fn, 'wb') as f: f.write(cab_data) # inform the plugin loader ploader.file_modified(fn) # update the database fw.checksum_signed = hashlib.sha1(cab_data).hexdigest() fw.signed_timestamp = datetime.datetime.utcnow() db.session.commit()
def test_invalid_version_format(self): cabarchive = CabArchive() cabarchive['firmware.bin'] = _get_valid_firmware() cabarchive['firmware.metainfo.xml'] = _get_valid_metainfo(version_format='foo') with self.assertRaises(MetadataInvalid): ufile = UploadedFile() ufile.parse('foo.cab', cabarchive.save())
def test_autogenerated(self): cabarchive = CabArchive() cabarchive['0x0962_nonsecure.bin'] = _get_valid_firmware() cabarchive['NVM0.metainfo.xml'] = _get_generated_metainfo() ufile = UploadedFile() _add_version_formats(ufile) ufile.parse('foo.cab', cabarchive.save())
def test_missing_firmware(self): cabarchive = CabArchive() cabarchive['firmware123.bin'] = _get_valid_firmware() cabarchive['firmware.metainfo.xml'] = _get_valid_metainfo() with self.assertRaises(MetadataInvalid): ufile = UploadedFile() ufile.parse('foo.cab', cabarchive.save())
def test_invalid_type(self): cabarchive = CabArchive() cabarchive['firmware.bin'] = _get_valid_firmware() with self.assertRaises(FileNotSupported): ufile = UploadedFile() _add_version_formats(ufile) ufile.parse('foo.doc', cabarchive.save())
def test_uncompressed(self): cabarchive = CabArchive() cabarchive['README.txt'] = CabFile(b'foofoofoofoofoofoofoofoo') cabarchive['firmware.bin'] = CabFile(b'barbarbarbarbarbarbarbar') buf = cabarchive.save() self.assertEqual(len(buf), 156) self.assertEqual(hashlib.sha1(buf).hexdigest(), '676654685d6b5918d68081a786ae1d4dbfeb5e01')
def test_compressed(self): cabarchive = CabArchive() cabarchive['README.txt'] = CabFile(b'foofoofoofoofoofoofoofoo') cabarchive['firmware.bin'] = CabFile(b'barbarbarbarbarbarbarbar') buf = cabarchive.save(compress=True) self.assertEqual(len(buf), 122) self.assertEqual(hashlib.sha1(buf).hexdigest(), '74e94703c403aa93b16d01b088eb52e3a9c73288')
def test_metainfo_invalid(self): cabarchive = CabArchive() cabarchive['firmware.bin'] = _get_valid_firmware() cabarchive['firmware.metainfo.xml'] = CabFile(b'<compoXXXXnent/>') with self.assertRaises(MetadataInvalid): ufile = UploadedFile() ufile.parse('foo.cab', cabarchive.save())
def test_metainfo_missing(self): cabarchive = CabArchive() cabarchive['firmware.bin'] = _get_valid_firmware() with self.assertRaises(MetadataInvalid): ufile = UploadedFile() _add_version_formats(ufile) ufile.parse('foo.cab', cabarchive.save())
def test_release_date(self): cabarchive = CabArchive() cabarchive['firmware.bin'] = _get_valid_firmware() cabarchive['firmware.metainfo.xml'] = _get_alternate_metainfo() ufile = UploadedFile() _add_version_formats(ufile) ufile.parse('foo.cab', cabarchive.save()) self.assertEqual(ufile.fw.mds[0].release_timestamp, 1562025600)
def test_metadata(self): cabarchive = CabArchive() cabarchive['firmware.bin'] = _get_valid_firmware() cabarchive['firmware.metainfo.xml'] = _get_valid_metainfo() ufile = UploadedFile() ufile.parse('foo.cab', cabarchive.save()) self.assertTrue(ufile.fw.mds[0].inhibit_download) self.assertTrue(ufile.fw.mds[0].version_format == 'quad')
def test_valid_path_back(self): cabarchive = CabArchive() cabarchive['DriverPackage\\firmware.bin'] = _get_valid_firmware() cabarchive['DriverPackage\\firmware.metainfo.xml'] = _get_valid_metainfo() ufile = UploadedFile() ufile.parse('foo.cab', cabarchive.save()) cabarchive2 = ufile.cabarchive_repacked self.assertIsNotNone(cabarchive2['firmware.bin']) self.assertIsNotNone(cabarchive2['firmware.metainfo.xml'])
def test_inf_invalid(self): cabarchive = CabArchive() cabarchive['firmware.bin'] = _get_valid_firmware() cabarchive['firmware.metainfo.xml'] = CabFile(b'<component/>') cabarchive['firmware.inf'] = CabFile(b'fubar') with self.assertRaises(MetadataInvalid): ufile = UploadedFile() _add_version_formats(ufile) ufile.parse('foo.cab', cabarchive.save())
def test_compressed(self): with open("data/compressed.cab", "rb") as f: old = f.read() arc = CabArchive() arc.parse(old) cff = arc.find_file("*.txt") self.assertEqual(cff.buf, b"test123") _check_range(arc.save(compress=True), old)
def test_release_mentions_file(self): cabarchive = CabArchive() cabarchive['firmware.bin'] = _get_valid_firmware() cabarchive['README.txt'] = _get_valid_firmware() cabarchive['firmware.metainfo.xml'] = \ _get_valid_metainfo(release_description='See README.txt for details.') with self.assertRaises(MetadataInvalid): ufile = UploadedFile() ufile.parse('foo.cab', cabarchive.save())
def test_invalid_bom(self): cabarchive = CabArchive() cabarchive['firmware.bin'] = _get_valid_firmware() cabarchive['firmware.metainfo.xml'] = CabFile(b'\xEF\xBB\xBF<?xml version="1.0" encoding="UTF-8"?>\n' b'<component type="firmware"/>\n') with self.assertRaises(MetadataInvalid): ufile = UploadedFile() _add_version_formats(ufile) ufile.parse('foo.cab', cabarchive.save())
def test_valid(self): cabarchive = CabArchive() cabarchive['firmware.bin'] = _get_valid_firmware() cabarchive['firmware.metainfo.xml'] = _get_valid_metainfo() ufile = UploadedFile() _add_version_formats(ufile) ufile.parse('foo.cab', cabarchive.save()) cabarchive2 = ufile.cabarchive_repacked self.assertIsNotNone(cabarchive2['firmware.bin']) self.assertIsNotNone(cabarchive2['firmware.metainfo.xml'])
def test_invalid_xml_header(self): cabarchive = CabArchive() cabarchive['firmware.bin'] = _get_valid_firmware() cabarchive['firmware.metainfo.xml'] = CabFile(b'<!-- Copyright 2015 Richard Hughes <*****@*****.**> -->\n' b'<?xml version="1.0" encoding="UTF-8"?>\n' b'<component type="firmware"/>\n') with self.assertRaises(MetadataInvalid): ufile = UploadedFile() _add_version_formats(ufile) ufile.parse('foo.cab', cabarchive.save())
def test_simple(self): with open("data/simple.cab", "rb") as f: old = f.read() arc = CabArchive() arc.parse(old) cff = arc["test.txt"] self.assertEqual(cff.filename, "test.txt") self.assertEqual(cff.buf, b"test123") self.assertEqual(len(cff.buf), 7) self.assertEqual(cff.date.year, 2015) _check_range(arc.save(), old)
def test_utf8(self): with open("data/utf8.cab", "rb") as f: old = f.read() arc = CabArchive() arc.parse(old) cff = arc.find_file("tést.dat") self.assertEqual(cff.filename, "tést.dat") self.assertEqual(cff.buf, "tést123".encode()) self.assertEqual(len(cff.buf), 8) self.assertEqual(cff.date.year, 2015) _check_range(arc.save(), old)
def test_multiple_metainfo_same_firmware(self): cabarchive = CabArchive() cabarchive['firmware.bin'] = _get_valid_firmware() cabarchive['firmware1.metainfo.xml'] = _get_valid_metainfo() cabarchive['firmware2.metainfo.xml'] = _get_valid_metainfo() ufile = UploadedFile() ufile.parse('foo.cab', cabarchive.save()) cabarchive2 = ufile.cabarchive_repacked self.assertIsNotNone(cabarchive2['firmware.bin']) self.assertIsNotNone(cabarchive2['firmware1.metainfo.xml']) self.assertIsNotNone(cabarchive2['firmware2.metainfo.xml'])
def test_extra_files(self): cabarchive = CabArchive() cabarchive['firmware.bin'] = _get_valid_firmware() cabarchive['firmware.metainfo.xml'] = _get_valid_metainfo() cabarchive['README.txt'] = CabFile(b'fubar') ufile = UploadedFile() ufile.parse('foo.cab', cabarchive.save()) cabarchive2 = ufile.cabarchive_repacked self.assertIsNotNone(cabarchive2['firmware.bin']) self.assertIsNotNone(cabarchive2['firmware.metainfo.xml']) with self.assertRaises(KeyError): self.assertIsNotNone(cabarchive2['README.txt'])
def test_valid_with_ignored_inf(self): cabarchive = CabArchive() cabarchive['firmware.bin'] = _get_valid_firmware() cabarchive['firmware.metainfo.xml'] = _get_valid_metainfo(enable_inf_parsing=False) cabarchive['firmware.inf'] = CabFile(b'fubar') ufile = UploadedFile() _add_version_formats(ufile) ufile.parse('foo.cab', cabarchive.save()) cabarchive2 = ufile.cabarchive_repacked self.assertIsNotNone(cabarchive2['firmware.bin']) self.assertIsNotNone(cabarchive2['firmware.metainfo.xml']) self.assertIsNotNone(cabarchive2['firmware.inf'])
def test_large_compressed(self): with open("data/large-compressed.cab", "rb") as f: old = f.read() arc = CabArchive() arc.parse(old) cff = arc.find_files("random.bin")[0] self.assertEqual(len(cff.buf), 0xFFFFF) self.assertEqual( hashlib.sha1(cff.buf).hexdigest(), "8497fe89c41871e3cbd7955e13321e056dfbd170", ) _check_range(arc.save(compress=True), old)
def test_create_compressed(self): cabarchive = CabArchive() # make predictable dt_epoch = datetime.datetime.fromtimestamp(0, datetime.timezone.utc) cabarchive["README.txt"] = CabFile(b"foofoofoofoofoofoofoofoo", mtime=dt_epoch) cabarchive["firmware.bin"] = CabFile(b"barbarbarbarbarbarbarbar", mtime=dt_epoch) buf = cabarchive.save(compress=True) self.assertEqual(len(buf), 122) self.assertEqual( hashlib.sha1(buf).hexdigest(), "74e94703c403aa93b16d01b088eb52e3a9c73288")
def _sign_fw(fw): # load the .cab file download_dir = app.config['DOWNLOAD_DIR'] fn = os.path.join(download_dir, fw.filename) try: with open(fn, 'rb') as f: cabarchive = CabArchive(f.read()) except IOError as e: raise NotImplementedError('cannot read %s: %s' % (fn, str(e))) # create Jcat file jcatfile = JcatFile() # sign each component in the archive print('Signing: %s' % fn) for md in fw.mds: try: # create Jcat item with SHA1 and SHA256 checksum blob cabfile = cabarchive[md.filename_contents] jcatitem = jcatfile.get_item(md.filename_contents) jcatitem.add_blob(JcatBlobSha1(cabfile.buf)) jcatitem.add_blob(JcatBlobSha256(cabfile.buf)) # sign using plugins for blob in ploader.archive_sign(cabfile.buf): # add GPG only to archive for backwards compat with older fwupd if blob.kind == JcatBlobKind.GPG: fn_blob = md.filename_contents + '.' + blob.filename_ext cabarchive[fn_blob] = CabFile(blob.data) # add to Jcat file too jcatitem.add_blob(blob) except KeyError as _: raise NotImplementedError('no {} firmware found'.format( md.filename_contents)) # rewrite the metainfo.xml file to reflect latest changes and sign it for md in fw.mds: # write new metainfo.xml file component = _generate_metadata_mds([md], metainfo=True) blob_xml = b'<?xml version="1.0" encoding="UTF-8"?>\n' + \ ET.tostring(component, encoding='UTF-8', xml_declaration=False, pretty_print=True) _show_diff(cabarchive[md.filename_xml].buf, blob_xml) cabarchive[md.filename_xml].buf = blob_xml # sign it jcatitem = jcatfile.get_item(md.filename_xml) jcatitem.add_blob(JcatBlobSha1(blob_xml)) jcatitem.add_blob(JcatBlobSha256(blob_xml)) for blob in ploader.archive_sign(blob_xml): jcatitem.add_blob(blob) # write jcat file if jcatfile.items: cabarchive['firmware.jcat'] = CabFile(jcatfile.save()) # overwrite old file cab_data = cabarchive.save() with open(fn, 'wb') as f: f.write(cab_data) # inform the plugin loader ploader.file_modified(fn) # update the download size for md in fw.mds: md.release_download_size = len(cab_data) # update the database fw.checksum_signed_sha1 = hashlib.sha1(cab_data).hexdigest() fw.checksum_signed_sha256 = hashlib.sha256(cab_data).hexdigest() fw.signed_timestamp = datetime.datetime.utcnow() db.session.commit()
def test_create(self): # create new archive arc = CabArchive() arc.set_id = 0x0622 # first example cff = CabFile() cff.buf = (b"#include <stdio.h>\r\n\r\nvoid main(void)\r\n" b'{\r\n printf("Hello, world!\\n");\r\n}\r\n') cff.date = datetime.date(1997, 3, 12) cff.time = datetime.time(11, 13, 52) cff.is_arch = True arc["hello.c"] = cff # second example cff = CabFile() cff.buf = (b"#include <stdio.h>\r\n\r\nvoid main(void)\r\n" b'{\r\n printf("Welcome!\\n");\r\n}\r\n\r\n') cff.date = datetime.date(1997, 3, 12) cff.time = datetime.time(11, 15, 14) cff.is_arch = True arc["welcome.c"] = cff # verify data = arc.save(False) with open("/tmp/test.cab", "wb") as f: f.write(data) expected = ( b"\x4D\x53\x43\x46\x00\x00\x00\x00\xFD\x00\x00\x00\x00\x00\x00\x00" b"\x2C\x00\x00\x00\x00\x00\x00\x00\x03\x01\x01\x00\x02\x00\x00\x00" b"\x22\x06\x00\x00\x5E\x00\x00\x00\x01\x00\x00\x00\x4D\x00\x00\x00" b"\x00\x00\x00\x00\x00\x00\x6C\x22\xBA\x59\x20\x00\x68\x65\x6C\x6C" b"\x6F\x2E\x63\x00\x4A\x00\x00\x00\x4D\x00\x00\x00\x00\x00\x6C\x22" b"\xE7\x59\x20\x00\x77\x65\x6C\x63\x6F\x6D\x65\x2E\x63\x00\xBD\x5A" b"\xA6\x30\x97\x00\x97\x00\x23\x69\x6E\x63\x6C\x75\x64\x65\x20\x3C" b"\x73\x74\x64\x69\x6F\x2E\x68\x3E\x0D\x0A\x0D\x0A\x76\x6F\x69\x64" b"\x20\x6D\x61\x69\x6E\x28\x76\x6F\x69\x64\x29\x0D\x0A\x7B\x0D\x0A" b"\x20\x20\x20\x20\x70\x72\x69\x6E\x74\x66\x28\x22\x48\x65\x6C\x6C" b"\x6F\x2C\x20\x77\x6F\x72\x6C\x64\x21\x5C\x6E\x22\x29\x3B\x0D\x0A" b"\x7D\x0D\x0A\x23\x69\x6E\x63\x6C\x75\x64\x65\x20\x3C\x73\x74\x64" b"\x69\x6F\x2E\x68\x3E\x0D\x0A\x0D\x0A\x76\x6F\x69\x64\x20\x6D\x61" b"\x69\x6E\x28\x76\x6F\x69\x64\x29\x0D\x0A\x7B\x0D\x0A\x20\x20\x20" b"\x20\x70\x72\x69\x6E\x74\x66\x28\x22\x57\x65\x6C\x63\x6F\x6D\x65" b"\x21\x5C\x6E\x22\x29\x3B\x0D\x0A\x7D\x0D\x0A\x0D\x0A") _check_range(bytearray(data), bytearray(expected)) # use cabextract to test validity try: self.assertEqual( subprocess.call(["cabextract", "--test", "/tmp/test.cab"]), 0) except FileNotFoundError as _: pass # check we can parse what we just created arc = CabArchive() with open("/tmp/test.cab", "rb") as f: arc.parse(f.read()) # add an extra file arc["test.inf"] = CabFile(b"$CHICAGO$") # save with compression with open("/tmp/test.cab", "wb") as f: f.write(arc.save(True)) # use cabextract to test validity try: self.assertEqual( subprocess.call(["cabextract", "--test", "/tmp/test.cab"]), 0) except FileNotFoundError as _: pass