{"id":24961117,"url":"https://github.com/awwad/uptane","last_synced_at":"2025-04-10T21:22:47.941Z","repository":{"id":90353586,"uuid":"65843523","full_name":"awwad/uptane","owner":"awwad","description":"Uptane, security framework for automotive updates","archived":false,"fork":false,"pushed_at":"2019-03-29T16:53:50.000Z","size":5389,"stargazers_count":10,"open_issues_count":3,"forks_count":42,"subscribers_count":3,"default_branch":"develop","last_synced_at":"2025-03-24T18:52:30.805Z","etag":null,"topics":["metadata","security","tuf","updates","vehicle"],"latest_commit_sha":null,"homepage":"https://uptane.github.io/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/awwad.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-08-16T18:19:48.000Z","updated_at":"2024-08-11T04:07:28.000Z","dependencies_parsed_at":null,"dependency_job_id":"41b49baa-8a55-4b3b-86f4-78cbfaab934d","html_url":"https://github.com/awwad/uptane","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/awwad%2Fuptane","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/awwad%2Fuptane/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/awwad%2Fuptane/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/awwad%2Fuptane/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/awwad","download_url":"https://codeload.github.com/awwad/uptane/tar.gz/refs/heads/develop","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248299072,"owners_count":21080455,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["metadata","security","tuf","updates","vehicle"],"created_at":"2025-02-03T08:45:34.799Z","updated_at":"2025-04-10T21:22:47.933Z","avatar_url":"https://github.com/awwad.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# [Uptane](https://uptane.github.io): Securing Software Updates for Automobiles\n\nReference Implementation and demonstration code\n\n[![Build Status](https://travis-ci.org/uptane/uptane.png?branch=develop)](https://travis-ci.org/uptane/uptane?branch=develop) [![Coverage Status](https://coveralls.io/repos/github/uptane/uptane/badge.svg)](https://coveralls.io/github/uptane/uptane?branch=develop)\n--------------------------------\n\n# Documentation\nExtensive documentation on design can be found in the following documents:\n\n* [Uptane Standard](https://uptane.github.io/uptane-standard/uptane-standard.html)\n* [Deployment Considerations](https://docs.google.com/document/d/17wOs-T7mugwte5_Dt-KLGMsp-3_yAARejpFmrAMefSE/edit?usp=sharing)\n\nThe project's [maintainers, contribution policies, and how-tos for submitting\nissues, security audits, etc. are visible in PROJECT.md](PROJECT.md)\n\nNote that the [Uptane Standard](https://uptane.github.io/uptane-standard/uptane-standard.html) is the definitive guide for how to implement Uptane.  If the reference implementation and the standard disagree, the standard is authoritative.  Any mismatches should be filed as issues in the issue tracker.\n\nThe reference implementation will describe which version of the standard it implements, once official standards releases have occurred.  \n\n\n# Instructions on use of the Uptane demonstration code\n\nBelow are the instructions on use of the Uptane demonstration and reference\nimplementation code, divided into these sections:\n\n* [0: Installation](#0-installation)\n* [1: Starting the Demo](#1-starting-the-demo)\n* [2: Delivering an Update](#2-delivering-an-update)\n* [3: Blocking Attacks](#3-blocking-attacks)\n  * [3.1: Arbitrary Package Attack on Director Repository without Compromised Keys](#31-arbitrary-package-attack-on-director-repository-without-compromised-keys)\n  * [3.2: Arbitrary Package Attack on Image Repository without Compromised Keys](#32-arbitrary-package-attack-on-image-repository-without-compromised-keys)\n  * [3.3: Replay Attack without Compromised Keys](#33-replay-attack-without-compromised-keys)\n  * [3.4: Arbitrary Package Attack with a Compromised Director Key](#34-arbitrary-package-attack-with-a-compromised-director-key)\n  * [3.5: Compromise Both Repositories Simultaneously to Serve Arbitrary Package](#35-compromise-both-repositories-simultaneously-to-server-arbitrary-package)\n  * [3.6: Recover from Major Key Compromise](#36-recover-from-major-key-compromise)\n  * [3.7: Arbitrary Package Attack with Revoked Keys](#37-arbitrary-package-attack-with-revoked-keys)\n* [Testing](#testing)\n\n\n# 0: Installation\nUptane supports Python2 and Python3. As usual for Python, [virtual environments](https://python-docs.readthedocs.io/en/latest/dev/virtualenvs.html) are recommended for development and testing, but not necessary.\n\nSome development libraries are necessary to install some of Uptane's dependencies. If your system uses apt, the command to install them will be:\n```shell\n$ sudo apt-get install build-essential libssl-dev libffi-dev python-dev python3-dev\n```\n\nFedora-based distributions can instead install these libraries with dnf.\n```shell\n$ dnf install redhat-rpm-config openssl-devel libffi-devel python-devel python3-devel\n```\n\nOS X users can instead install these header libraries with the [Homebrew](http://brew.sh/) package manager.\n```shell\n$ brew install python\n$ brew install libffi\n```\n\nTo download and install the Uptane code and its dependencies, run the following:\n```shell\n$ git clone https://github.com/uptane/uptane\n$ cd uptane\n$ pip install -r dev-requirements.txt\n```\n\n#### Updates\nWhen updating Uptane code, please reinstall its dependencies, as the\ncorresponding TUF fork may be updated:\n`pip install --force-reinstall -r dev-requirements.txt`\n\n#### Metadata format\nNote that the demonstration now operates using ASN.1 / DER format and encoding\nfor metadata files by default. If desired, this can be switched to JSON (which\nresults in human-readable metadata files) by changing the\ntuf.conf.METADATA_FORMAT option in `uptane/__init__.py`, from 'der' to 'json'\n`tuf.conf.METADATA_FORMAT = 'json'`\n\n\n#### Install command-line audio player (optional)\nIf you want the demo to play notification sounds you need one of the following audio player command line utilities on your path:\n- mplayer (available for all major operating systems)\n- omxplayer (built-in on Raspbian)\n- afplay (built-in on OS X)\n\n#### Troubleshooting\nIf you are running into errors or want to run unit and integration tests to\nbetter understand the workings of the reference implementation, see the\n[Testing](#testing) section at the bottom of this document.\n\n\n# 1: Starting the Demo\nThe code below is intended to be run in three or more consoles:\n- [WINDOW 1](#window-1-the-uptane-services): Python shell for the Uptane services\n- [WINDOW 2](#window-2-the-primary-clients): Python shell for a Primary client in the vehicle. This fetches images and metadata from the repositories via HTTP, and communicates with the Director service, Timeserver, and any Secondaries via XMLRPC. (More of these can be run, simulating more vehicles with one Primary each.)\n- [WINDOW 3](#window-3-the-secondary-clients): Python shell for a Secondary in the vehicle. This communicates directly only with the Primary via XMLRPC, and will perform full metadata verification. (More of these can be run, simulating more ECUs in one or more vehicles.)\n\n\n\n### WINDOW 1: the Uptane services\nThese instructions start a demonstration version of the three services that\nrun OEM-side (or supplier-side, or fleet-side): the Image Repository,\nthe Director, and the Timeserver.\n\n\nThe Uptane documentation explains each of these services in greater detail,\nbut in brief:\n\n**The Image Repository** is the main repository for images and general metadata\nabout them.\n\n**The Director** generates metadata for specific\nvehicles indicating which ECUs should install what firmware (validated against\nand obtained from the Image Repository). It also receives and validates\nVehicle Manifests from Primaries, and the ECU Manifests from Secondaries\nthat have been bundled in the Vehicle Manifests, which capture trustworthy\ninformation about what software is running on the ECUs, along with, optionally,\nsigned reports of any attacks observed by those ECUs.\n\n**The Timeserver** is a simple service that receives requests for signed\ntimes, each bundled by a vehicle Primary, and produces a signed attestation\nthat includes the request tokens each Secondary ECU sent to its Primary, so\nthat each ECU can better establish that it is not being tricked into accepting\na false or very old time.\n\n\nFrom within the root `uptane/` directory of the downloaded code (which contains e.g. the `setup.py` file), run the following command. (Any version of Python \u003e 2.7\nshould do. We test on 2.7, 3.5, and 3.6.)\n\n```Bash\n$ python -i demo/start_servers.py\n```\n\n\nAfter that, proceed to the following Windows to prepare clients.\nOnce those are ready, you can perform a variety of modifications and attacks.\nExamples will be discussed below in the\n[Delivering an Update](#2-delivering-an-update) and\n[Blocking Attacks](#blocking-attacks) sections.\n\n\n\n### WINDOW 2(+): the Primary client(s):\n(Image Repo, Director, and Timeserver must already have finished starting up.)\nThe Primary client started below is likely to run on a more capable and\nconnected ECU in the vehicle - potentially the head unit / infotainment. It will\nobtain metadata and images from the Image Repository as instructed by the Director\nand distribute them appropriately to other, Secondary ECUs in the vehicle,\nand it will receive ECU Manifests indicating the software on each Secondary ECU,\nand bundle these into a Vehicle Manifest which it will send to the Director.\n\nOpen a Python shell in a new terminal window and then run the following:\n\n```python\n\u003e\u003e\u003e import demo.demo_primary as dp\n\u003e\u003e\u003e dp.clean_slate() # sets up a fresh Primary that has never been updated\n\u003e\u003e\u003e dp.update_cycle()\n```\n\nThe Primary's update_cycle() call:\n- fetches and validates all signed metadata for the vehicle, from the Director and Image repositories\n- fetches all images that the Director instructs this vehicle to install, excluding any that do not exactly match corresponding images on the Image repository. Any images fetched from the repositories that do not match validated metadata are discarded.\n- queries the Timeserver for a signed attestation about the current time, including in it any nonces sent by Secondaries, so that Secondaries may trust that the time returned is at least as recent as their sent nonce\n- generates a Vehicle Version Manifest with some vehicle metadata and all ECU Version Manifests received from Secondaries, describing currently installed images, most recent times available to each ECU, and reports of any attacks observed by Secondaries (can also be called directly: `dp.generate_signed_vehicle_manifest()`)\n- sends that Vehicle Version Manifest to the Director (can also be called directly: `dp.submit_vehicle_manifest_to_director()`)\n\nIf you wish to run the demo with multiple vehicles (one Primary each), you can open a Python shell in a new terminal\nwindow for each vehicle's Primary and provide a unique VIN and ECU Serial for each of them. Because each Secondary will need to communicate with the correct Primary in this demo, find port that is chosen in the Primary's initialization (when `dp.clean_slate()` is called) and make note of it so that it can be provided to any Secondaries you set up in a moment. The message will be e.g. \"Primary will now listen on port 30702\")\nExample setting up a different Primary for a different vehicle:\n```python\n\u003e\u003e\u003e import demo.demo_primary as dp\n\u003e\u003e\u003e dp.clean_slate(vin='112', ecu_serial='PRIMARY_ECU_2')\n\u003e\u003e\u003e dp.update_cycle()\n```\n\n\n\n### WINDOW 3(+): the Secondary client(s):\n(The following assumes that the Image Repository, Director, Timeserver, and\nPrimary have finished starting up and are hosting/listening.)\nHere, we start a single Secondary ECU and generate a signed ECU Manifest\nwith information about the \"firmware\" that it is running, which we send to the\nPrimary.\n\nOpen a Python shell in a new terminal window and then run the following:\n\n```python\n\u003e\u003e\u003e import demo.demo_secondary as ds\n\u003e\u003e\u003e ds.clean_slate()\n\u003e\u003e\u003e ds.update_cycle()\n```\n\nOptionally, multiple windows with different Secondary clients can be run simultaneously. In each additional window, you can run the same calls as above to set up a new ECU in the same, default vehicle by modifying the clean_slate() call to include a distinct ECU Serial. e.g. `ds.clean_slate(ecu_serial='33333')`\n\nIf the Secondary is in a different vehicle from the default vehicle, this call should look like:\n`ds.clean_slate(vin='vehicle_id_here', ecu_serial='ecu_serial_here', primary_port=30702)`, providing a VIN for the new vehicle, a unique ECU Serial, and indicating the port listed by this Secondary's Primary when that Primary initialized (e.g. \"Primary will now listen on port 30702\").\n\nThe Secondary's update_cycle() call:\n- fetches and validates the signed metadata for the vehicle from the Primary\n- fetches any image that the Primary assigns to this ECU, validating that against the instructions of the Director in the Director's metadata, and against file info available in the Image Repository's metadata. If the image from the Primary does not match validated metadata, it is discarded.\n- fetches the latest Timeserver attestation from the Primary, checking for the nonce this Secondary last sent. If that nonce is included in the signed attestation from the Timeserver and the signature checks out, this time is saved as valid and reasonably recent.\n- generates an ECU Version Manifest that indicates the secure hash of the image currently installed on this Secondary, the latest validated times, and a string describing attacks detected (can also be called directly: `ds.generate_signed_ecu_manifest()`)\n- submits the ECU Version Manifest to the Primary (can also be called directly: `ds.submit_ecu_manifest_to_primary()`)\n\n\n\n# 2: Delivering an Update\nTo deliver an Update via Uptane, you'll need to add the firmware image to the Image repository, then assign it to a vehicle\nand ECU in the Director repository. Then, the Primary will obtain the new firmware, and the Secondary will update from the\nPrimary.\n\nExecute the following code in the Uptane services window (WINDOW 1) to create a\nnew firmware file, generate metadata about it, sign that metadata with the\nappropriate keys (assigned by delegations in the Image Repository), and host\nthe image and metadata on the Image Repository.\n\n```python\n\u003e\u003e\u003e firmware_fname = filepath_in_repo = 'firmware.img'\n\u003e\u003e\u003e open(firmware_fname, 'w').write('Fresh firmware image')\n\u003e\u003e\u003e di.add_target_to_imagerepo(firmware_fname, filepath_in_repo)\n\u003e\u003e\u003e di.write_to_live()\n```\n\nTo assign the new image to the ecu 'TCUdemocar' on vehicle 'democar', run\nthe following in the Uptane services window (WINDOW 1):\n```python\n\u003e\u003e\u003e vin='democar'; ecu_serial='TCUdemocar'\n\u003e\u003e\u003e dd.add_target_to_director(firmware_fname, filepath_in_repo, vin, ecu_serial)\n\u003e\u003e\u003e dd.write_to_live(vin_to_update=vin)\n```\n\nNext, you can update the Primary. In WINDOW 2:\n```python\n\u003e\u003e\u003e dp.update_cycle()\n```\n\nWhen the Primary has finished, you can update the Secondary. In WINDOW 3:\n```python\n\u003e\u003e\u003e ds.update_cycle()\n```\n\nYou should see an Updated banner on the Secondary, indicating a successful, validated update.\n\n\n\n# 3: Blocking Attacks\nUptane is designed to secure the software updates delivered between repositories and vehicles.  [Section\n7.3](https://docs.google.com/document/d/1pBK--40BCg_ofww4GES0weYFB6tZRedAjUy6PJ4Rgzk/edit#heading=h.jta2pcxo2frp) of the [Uptane Design Overview](https://docs.google.com/document/d/1pBK--40BCg_ofww4GES0weYFB6tZRedAjUy6PJ4Rgzk/edit?usp=sharing) covers all of the known attacks in more detail.  We begin this section with a demonstration\nof the Arbitrary Package Attack.\n\n\n\n### 3.1: Arbitrary Package Attack on Director Repository without Compromised Keys\nThis is a simple attack simulating a Man in the Middle that provides a malicious image file. In this attack, the\nattacker does not have the keys to correctly sign new metadata (and so it is an exceptionally basic attack).\n\nIn the Uptane services window (1):\n```python\n\u003e\u003e\u003e dd.mitm_arbitrary_package_attack(vin, firmware_fname)\n```\n\nAs a result of the attack above, the Director will instruct the secondary client in the vehicle to install *firmware.img*.\nSince this file is not on (and validated by) the Image Repository, the Primary will refuse to download it\n(and a Full Verification Secondary would likewise refuse it even if a compromised Primary delivered it\nto the Secondary).\n\nIn WINDOW 1, run:\n```python\n\u003e\u003e\u003e dp.update_cycle()\n```\n\nNow, when the Primary runs dp.update_cycle(), it'll display the DEFENDED banner and play a sound clip, as it's\nable to discard the manipulated file without even sending it to the Secondary.\n\nTo resume experimenting with the repositories, run this script to put the\nrepository back in a normal state (undoing what the attack did) by running the\nfollowing in the services window (1):\n```python\n\u003e\u003e\u003e dd.undo_mitm_arbitrary_package_attack(vin, firmware_fname)\n```\n\nIf the primary client runs an update_cycle() after the restoration of the Director repository, *firmware.img*\nshould updated successfully.\n\n\n\n### 3.2: Arbitrary Package Attack on Image Repository without Compromised Keys\nIn the previous section, the firmware available on the director repository was replaced with a malicious one.\nWhat if the image repository is corrupted with a malicious firmware?\n\nRun the following in WINDOW 1:\n```python\n\u003e\u003e\u003e di.mitm_arbitrary_package_attack(firmware_fname)\n```\n\nYou can see the update once again proceeding normally by running this in the\nPrimary's window (2):\n```python\n\u003e\u003e\u003e dp.update_cycle()\n```\n\n\nThe result is the same: The primary client is expected to also discard the\nmalicious `firmware.img` downloaded from the Image repository\nand print a DEFENDED banner.  If you inspect messages further up in\nthe console (above the banner) to get more detail, you should\nfind the following:\n\n```\nDownloading: u'http://localhost:30301/targets/firmware.img'\nDownloaded 14 bytes out of the expected 14 bytes.\nNot decompressing http://localhost:30301/targets/firmware.img\nUpdate failed from http://localhost:30301/targets/firmware.img.\nBadHashError\nFailed to update /firmware.img from all mirrors: {u'http://localhost:30301/targets/firmware.img': BadHashError()}\nDownloading: u'http://localhost:30401/democar/targets/firmware.img'\nCould not download URL: u'http://localhost:30401/democar/targets/firmware.img'\n```\n\nUptane detected that the image retrieved did not have a hash matching what the\nsigned, validated metadata indicated we should expect.\n\nUndo the the arbitrary package attack so that subsequent demonstration sections\ncan proceed.\n\n```python\n\u003e\u003e\u003e di.undo_mitm_arbitrary_package_attack(firmware_fname)\n```\n\n### 3.3: Replay Attack without Compromised Keys\n\nWe next demonstrate a replay attack, where the client is given an older (and previously trusted)\nversion of metadata.  A replay attack could result in a rollback of installed\nsoftware, causing secondary clients to use older firmware than they\ncurrently trust. Rollback attacks in general are described in The\n[Deny Functionality subsection of the Design Overview](https://docs.google.com/document/d/1pBK--40BCg_ofww4GES0weYFB6tZRedAjUy6PJ4Rgzk/edit#heading=h.4mo91b3dvcqd).\n\nFirst, switch to the services window and copy the Timestamp role's\nmetadata, `timestamp.der` to a backup. This is what we'll roll back to in the\nattack.\nA function is available to perform this action:\n```python\n\u003e\u003e\u003e dd.backup_timestamp(vin)\n```\n\nNow, we update the metadata in the Director repository. In this case, a fairly\nempty update, writing a new `timestamp.der` and `snapshot.der` files. The backup\nwe saved earlier is now old by comparison.\n```python\n\u003e\u003e\u003e dd.write_to_live(vin)\n```\n\nIn the Primary's window (2), the Primary client now performs an update,\nretrieving the new metadata we generated.\n\n```python\n\u003e\u003e\u003e dp.update_cycle()\n```\n\nNext, in the services window, we simulate the replay attack, trying to\ndistribute the out-of-date metadata backed up earlier, effectively rolling back\nthe timestamp file to a previous version.\n```python\n\u003e\u003e\u003e dd.replay_timestamp(vin)\n```\n\nIf this old metadata is presented to the Primary, the Primary rejects it,\nbecause it has already validated newer metadata. When you run the below, you\nshould see a REPLAY banner. The console also logs the cause of the download\nfailure (ReplayedMetadataError exception). In the **Primary's window**:\n```python\n\u003e\u003e\u003e dp.update_cycle()\n...\nUpdate failed from http://localhost:30401/democar/metadata/timestamp.der.\nReplayedMetadataError\nFailed to update timestamp.der from all mirrors:\n{u'http://localhost:30401/democar/metadata/timestamp.der': ReplayedMetadataError()}\n```\n\nFinally, restore the valid, latest version of Timestamp metadata\n(`timestamp.der`) into place in the services window.\n```python\n\u003e\u003e\u003e dd.restore_timestamp(vin)\n```\n\n\n\n### 3.4: Arbitrary Package Attack with a Compromised Director Key\n\nThus far we have simulated a few attacks that have not depended on compromised keys.  In\nthe previous attacks, an attacker has presented unchanged, out-of-date data\n(replay, 3.3) or simply modified the images or metadata requested by a primary\nor secondary client, without having the right key to sign new metadata.\nConsequently, these clients have blocked the attacks because the malicious\nimages do not match what is listed in signed, trusted metadata.\n\nHowever, what happens if an attacker manages to obtain or make use of a\nrepository key and signs for a malicious image?  Is the client able to block a compromise\nof just the image repository?  What about a compromise of both the image and director\nrepositories?\n\nBoth repositories currently have metadata about 'firmware.img', which we added\nin the [2: Delivering an Update](#2-delivering-an-update) section.\n\nFor this attack, we'll modify `firmware.img` to include malicious content, and\nwill sign metadata certifying that malicious firmware file.\n\nTo simulate a compromised directory key, we simply sign for an updated\n\"firmware.img\" that includes malicious content (the phrase \"evil content\" in\nthis case), in the services window:\n\n```python\n\u003e\u003e\u003e dd.add_target_and_write_to_live(filename='firmware.img',\n    file_content='evil content', vin=vin, ecu_serial=ecu_serial)\n```\n\nThe primary client now attempts to download the malicious file.\n```python\n\u003e\u003e\u003e dp.update_cycle()\n```\n\nThe primary client should print a DEFENDED banner and provide the following error message: `The Director has instructed\nus to download a file that does not exactly match the Image Repository metadata. File: '/firmware.img'`\n\n\n\n### 3.5: Compromise Both Repositories Simultaneously to Serve Arbitrary Package\nIn 3.4, the Director repository has been compromised, but the malicious\nmetadata and firmware it distributes is rejected. Compromising the Director is\nnot enough to allow arbitrary package attacks against ECUs in the vehicle.\nThe Director can only instruct clients to install images validated by the Image\nRepository.\n\nBut what happens if, at the same time, the attacker is able to sign with a\nhigh-level image-signing key trusted by the Image Repository? (Note that these\nshould generally be offline keys and rarely need to be used.)\nWith the previous attack still in place, let's add:\n```python\n\u003e\u003e\u003e di.add_target_and_write_to_live(filename='firmware.img', file_content='evil content')\n```\n\nNow, when the the Primary and Secondary update, the malicious package will\nsuccessfully be installed!\n\nOn the **primary** client:\n```python\n\u003e\u003e\u003e dp.update_cycle()\n```\n\nOn the **secondary** client:\n```python\n\u003e\u003e\u003e ds.update_cycle()\n```\n\nNote, both the image and director repositories have been compromised. As a\nresult, unfortunately, in an attack of this kind Secondary would install this\nmalicious firmware.img, which neither the Primary nor the\nSecondary have any way of knowing is malicious, since every necessary key has\nsigned metadata for that image.\n\nFor demonstration purposes, the secondary detects that a malicious file is installed.  The secondary\nclient in the demo code prints a banner indicating that the *firmware.img* image was malicously\ninstalled: A malicious update has been installed! Arbitrary package attack successful:\nthis Secondary has been compromised! Image: 'new_firmware.img'\n\n##### A note on the difficulty of this attack\nTo improve resilience against repository compromises, the\n[Deployment Considerations document](https://docs.google.com/document/d/17wOs-T7mugwte5_Dt-KLGMsp-3_yAARejpFmrAMefSE)\nprovides many recommendations. In particular, the\n[key placement recommendations](https://docs.google.com/document/d/17wOs-T7mugwte5_Dt-KLGMsp-3_yAARejpFmrAMefSE/view#heading=h.k5rokxr32wv6)\nindicate that Targets keys for the Image Repository are best kept offline; it\nshould not be easy to compromise this top-level Image Repository Targets key\nand use it to sign malicious images, as this key should be needed only when\naltering delegations (to parties who may sign for particular subsets of images).\nDepending on the way you deploy the system, this may be only when establishing a\nrelationship with a new firmware supplier or revoking trust in them, for\nexample.\n\nA more limited attack of this sort is possible against a subset of available\nfirmware if rather than the top-level Image Repository targets keys, the\nattacker acquires the signing keys held by a party who has been delegated trust\nfor a subset of images, such as a supplier the Image Repository has elected to\ntrust to do the image signing for its own firmware releases to the Image\nRepository. While such delegated keys should also be held offline and need only\nbe used when the delegatee produces new firmware, it is easier to imagine such\na key being compromised than the Image Repository's top-level Targets role\nkey(s). The structure of Uptane is flexible enough to accommodate almost any\ntrust arrangement, allowing the impact of keys being lost to be limited to a\nsmall subset of images. Note that as before, this attack still requires also\ncompromising the Director repository's Targets role keys or Root role keys.\n\n\n\n\n### 3.6: Recover from Major Key Compromise\n\nIf a malicious attacker has laid hands on your online keys, or found a way to\nforce systems with such access to sign malicious metadata, trust in those keys\nshould be revoked, and new keys should be used in their place. These are likely\nto be the Timestamp and Snapshot role keys, and the Targets role keys in some circumstances (generally the Director). (More information on these roles and\ndeployment considerations are available in documentation\n[linked to above](#uptane).)\n\nOnce the servers have been recovered, it is easy to revoke any keys that may\nhave been compromised. We should also make sure that the targets and metadata\nin the repositories are correct again, in case things have been changed by the\nattacker while in control. In this demo, these two things are done like so:\n\nIn the services window:\n```\n\u003e\u003e\u003e di.revoke_compromised_keys()\n\u003e\u003e\u003e di.add_target_and_write_to_live(filename='firmware.img',\n        file_content='Fresh firmware image')\n\u003e\u003e\u003e dd.revoke_compromised_keys()\n\u003e\u003e\u003e dd.add_target_and_write_to_live(filename='firmware.img',\n    file_content='Fresh firmware image', vin=vin, ecu_serial=ecu_serial)\n```\n\nWe have just used the rarely-needed root keys of the two repositories to\nrevoke the potentially compromised Timestamp, Snapshot, and Targets keys from\nboth repositories. This is the first time they have needed to be used since the\nrepositories' creation at the beginning of this demo. Root keys should be\nkept offline, as discussed in\nthe [Uptane Deployment Considerations document](https://docs.google.com/document/d/17wOs-T7mugwte5_Dt-KLGMsp-3_yAARejpFmrAMefSE/edit?usp=sharing).\nIf trust in a key needs to be revoked, the keys at a level above it (or any\nlevel above that) can be used.\nIn the case of the top-level roles (like Timestamp, Snapshot, and Targets here),\nthat falls to Root to do. In the case of delegated Targets roles, the role\nthat delegates would do the revocation. So, for example, if you delegate\nthe ability to sign for firmware from a given vendor to the security team from\nthat vendor, and that team delegates in turn to a particular release manager,\nand that release manager's YubiKey falls into the sewers, the security team\nfrom the vendor can remove trust in it and trust a new key. Alternatively, the\ntop-level Targets role for the Image Repository could remove trust for the\nsecurity team from the vendor, etc.\n\nAny client that receives the new metadata will be able to validate the root key\nand will cease trusting the revoked keys.\n\nOn the **primary** client:\n```python\n\u003e\u003e\u003e dp.update_cycle()\n```\n\nOn the **secondary** client:\n```python\n\u003e\u003e\u003e ds.update_cycle()\n```\n\nAs ever, if a particular ECU has been compromised and arbitrary attacker code\nhas been executed on it, being certain that specific device is secure again\nwithout wiping it manually is difficult. Devices that have not been compromised\nin such an attack, however,\nshould thereafter be protected from the use of those compromised keys by an\nattacker.\n\n\n\n### 3.7: Arbitrary Package Attack with Revoked Keys\n\nWe should verify that the Primary does indeed reject metadata that's been\nsigned with revoked keys.  As noted in the previous section, the Primary and\nsecondaries automatically remove trust in revoked keys when they install the\nnew Root metadata.\n\nLet's begin the demonstration by generating metadata that is maliciously\nsigned with the keys revoked in the last section.\n\n```Python\n\u003e\u003e\u003e dd.sign_with_compromised_keys_attack(vin)\n```\n\n\nThe Primary attempts to download the maliciously-signed metadata...\n\n```Python\n\u003e\u003e\u003e dp.update_cycle()\n```\n\n... and detects a bad signature by displaying a DEFENDED banner.  The Primary\ndoes not trust the keys and signature specified in the metadata, as expected.\nIf you were to inspect the cause of the download failure, you'd find the\nfollowing exception:\n\n```\nDownloading: u'http://localhost:30401/democar/metadata/timestamp.der'\nDownloaded 202 bytes out of an upper limit of 16384 bytes.\nNot decompressing http://localhost:30401/democar/metadata/timestamp.der\nmetadata_role: u'timestamp'\nUpdate failed from http://localhost:30401/democar/metadata/timestamp.der.\nBadSignatureError\nFailed to update timestamp.der from all mirrors: {u'http://localhost:30401/democar/metadata/timestamp.der': BadSignatureError()}\nValid top-level metadata cannot be downloaded.  Unsafely update the Root metadata.\n```\n\nWe next restore metadata to the previously trusted state, where the compromised\nkeys had been revoked and where new keys were added for the Targets, Snapshot,\nand Timestamp roles.\n\n```Python\n\u003e\u003e\u003e dd.undo_sign_with_compromised_keys_attack(vin)\n```\n\nIf the Primary initiates an update cycle once again, it would appear to be\nup-to-date.  The metadata that was signed by the revoked keys should not\nhave been saved by the Primary.\n\n\n```Python\n# This call should indicate that the client is up-to-date.\n\u003e\u003e\u003e dp.update_cycle()\n```\n\n\n\n# Testing\n\nIf you are concerned that there may be installation issues, or have run into\nissues running the demo, or want to better understand the workings of\nthe reference implementation, or want a thorough test of whether or not the\nUptane reference implementation would work in a particular environment, you can\nrun Uptane's unit tests from the root uptane/ repository directory by invoking\n[tox](https://testrun.org/tox/) like so:\n```Bash\n$ tox\n```\n\nAlternatively, you can execute any of the unit tests in the tests directory\ndirectly, like so:\n```Bash\n$ python tests/test_secondary.py\n```\n\nOr you can run all tests with a particular encoding (default ASN.1/DER):\n```Bash\n$ python tests/runtests.py\n// Or:\n$ python tests/runtests.py json\n$ python tests/runtests.py der\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fawwad%2Fuptane","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fawwad%2Fuptane","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fawwad%2Fuptane/lists"}