Skip to content
This repository has been archived by the owner on Mar 29, 2022. It is now read-only.

Secondary partial verification #143

Open
wants to merge 43 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
48e9337
Started work on secondary partial verification
shikhar394 Nov 5, 2017
4b113d9
Code review changes
shikhar394 Dec 28, 2017
cd03e8a
Fixed a bug that raised error in json mode
shikhar394 Jan 2, 2018
61fb176
Changes made to make the targets metadata be verified using the direc…
shikhar394 Jan 6, 2018
376eaa8
- Added test for PV Secondaries : test_45_process_partial_metadata
shikhar394 Jan 10, 2018
ca9e583
Removed some left over depracated changes
shikhar394 Jan 10, 2018
6669b11
Fixing bug that stops the PV secondaries from working with .der format
shikhar394 Jan 11, 2018
bb755e8
Added instructions and details about PV secondaries in README.md
shikhar394 Jan 16, 2018
a588823
Fixed some style issues in the test_secondary.py
shikhar394 Jan 16, 2018
941dab1
Made variable name changes for uptane_asn1_codec and tuf_asn1_codec i…
shikhar394 Jan 16, 2018
18b431d
More testing for secondary.py | process_partial_metadata()
shikhar394 Jan 17, 2018
edcf05e
Sample metadata in DER format to support more tests added
shikhar394 Jan 17, 2018
d24ffde
Remove trailing whitespace in partial-verification secondary code
awwad Jan 30, 2018
5780258
Correct assertions (style / function use) in test_secondary
awwad Jan 30, 2018
e5c73be
DOC: Improve docstrings and comments in secondary.py
awwad Jan 30, 2018
fd34711
Update variable names for p-v secondaries (readability)
awwad Jan 30, 2018
585e2b3
Add rollback attack protection to new partial verification secondary …
awwad Jan 30, 2018
7e250b7
Add metadata expiration check to partial verification secondary code
awwad Jan 30, 2018
ca998ad
Support finding right sig from multiple sigs in p-v secondary code
awwad Jan 30, 2018
5dc56ee
Clarify code and comments and fix bugs from the last few commits
awwad Jan 30, 2018
c55c0e9
Preserve last Director-validated firmware info until replaced
awwad Feb 7, 2018
05c7328
DOC: Move README partial-verification instructions to new section
awwad Feb 8, 2018
70aea1e
DOC: Expand explanation in README of demo clients' update_cycle()
awwad Feb 8, 2018
51ed0cb
Make partial-verification Secondary respect tuf.conf.METADATA_FORMAT
awwad Feb 8, 2018
98ae7cb
Fix p-v Secondary test code given correction to format determination
awwad Feb 8, 2018
593e48e
Add test to p-v Secondary tests: missing Director pub key
awwad Feb 8, 2018
9f07341
DOC: Note state of test and sample files for secondary
awwad Feb 21, 2018
ce97644
Simplify some pv secondary test variables in test_secondary
awwad Feb 21, 2018
9539c8c
Correct use of tuf.ReplayedMetadataError (same PR)
awwad Feb 21, 2018
e26b12f
Test partial verification Secondary for expired and replayed metadata
awwad Feb 21, 2018
47b292c
DOC: Add clarifying comment to temp vars in test_secondary
awwad Feb 21, 2018
282c0ba
Error if Secondary receives time attest. before ever sending nonces
awwad Feb 21, 2018
c7299b7
Test time attestation validation by Secondary that never sent a nonce
awwad Feb 21, 2018
9b83a38
Additional testing of p-v Secondary signature checking
awwad Feb 21, 2018
700b4f0
Test p-v Secondary behavior if Director Targets file is missing
awwad Feb 21, 2018
8535118
DOC: Tidy up some demo comments in demo_secondary
awwad Feb 21, 2018
9755886
DOC: Clarify error conditions in process_partial_metadata docstring
awwad Feb 21, 2018
69310a8
Rename four sample / test data files used by test_secondary
awwad Feb 22, 2018
1c5bd28
DOC: Minor style adjustment and removal of whitespace change
awwad Feb 22, 2018
eb77939
Add some more sample Director Targets metadata for future use
awwad Feb 22, 2018
0f84590
Reorganize test data use in test_secondary
awwad Feb 22, 2018
83facbe
In test_secondary, give pv Sec metadata pointing to test firmware,
awwad Feb 22, 2018
43c1262
In test_secondary, test p-v Sec's validation of an image as well
awwad Feb 22, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 72 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,15 @@ Open a Python shell in a new terminal window and then run the following:

The Primary's update_cycle() call:
- fetches and validates all signed metadata for the vehicle, from the Director and Image repositories
- 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.
- reads the Director's Targets metadata to determine what targets the Director
has instructed this vehicle to install, storing the trustworthy target info for
each (hash, length, etc.), and comparing it to similar info from the Image
repository. A mismatch results in that installation instruction being ignored.
- fetches the target images indicated and compares them to the trusted info
(hash, length, etc.). Any images fetched
from the repositories that do not match validated metadata are discarded.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does Uptane deal with clients that are unable to successfully fetch a required image? For instance, a malicious attack where a crucial firmware instructed by the manufacturer is denied to a car.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At the level of our code, the same way as TUF: if a target can't be obtained, an error is raised indicating such.

If you're asking about a higher level, it would be up to firmware developers and car manufacturers to decide what behavior should result based an inability to update in a given incident.

- 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
- validates the Timeserver's time attestations for itself and updates its time
- 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()`)
- sends that Vehicle Version Manifest to the Director (can also be called directly: `dp.submit_vehicle_manifest_to_director()`)

Expand All @@ -177,10 +184,11 @@ Example setting up a different Primary for a different vehicle:
Primary have finished starting up and are hosting/listening.)
Here, we start a single Secondary ECU and generate a signed ECU Manifest
with information about the "firmware" that it is running, which we send to the
Primary.
Primary. Note that these instructions will demonstrate a full-verification
Secondary; for partial-verification Secondaries, see
[this section below](#partial-verification-secondaries).

Open a Python shell in a new terminal window and then run the following:

```python
>>> import demo.demo_secondary as ds
>>> ds.clean_slate()
Expand All @@ -194,8 +202,17 @@ If the Secondary is in a different vehicle from the default vehicle, this call s

The Secondary's update_cycle() call:
- fetches and validates the signed metadata for the vehicle from the Primary
- 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.
- 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.
- looks for target info (hash, length, etc.) in the now-validated Targets
metadata that includes this Secondary's ECU identifier, indicating an
instruction from the Director to install that target firmware
- fetches any image that the Primary assigns to this ECU, validating that
against the target info from the validated Director metadata. If the image from
the Primary does not match validated metadata, it is discarded.
- 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, the signature is correct and by the expected key, and the
time is more recent than the last validated time, this time is saved as valid,
allowing this Secondary to disregard any metadata with an expiration date
earlier than that time.
- 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()`)
- submits the ECU Version Manifest to the Primary (can also be called directly: `ds.submit_ecu_manifest_to_primary()`)

Expand Down Expand Up @@ -609,6 +626,56 @@ have been saved by the Primary.
```


# Partial-Verification Secondaries
The Secondaries described and employed above ran full verification, employing
the full suite of TUF and Uptane security checks. Uptane provides for a
less demanding alternative: partial-verification Secondaries. These perform
a few checks instead of the full set, at a measured cost to security, allowing
weaker ECUs to still operate as Uptane-conformant Secondaries.

The distinction is defined
[in Section 8.3 of the Implementation Specification](https://docs.google.com/document/d/1wjg3hl0iDLNh7jIRaHl3IXhwm0ssOtDje5NemyTBcaw/edit?pli=1#heading=h.22u629s8u37q).
Briefly, partial verification entails one signature check to validate metadata,
and one signature check whenever the minimum time is ratcheted forward (for
metadata expiration purposes). Partial-verification Secondaries need to know
the Director's Targets role public key and the Timeserver's public key.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a mechanism for acquiring the public key, or is it left to the implementer? Does the reference implementation provide a way for a PVS to acquire the Director's targets public key?

These are only questions that I ask myself while reading these standalone README edits.


You can run a demo partial-verification Secondary like so:
(Mind that you have [the Uptane services](#window-1-the-uptane-services) and
[a Primary client](#window-2-the-primary-clients) both running).

```python
>>> import demo.demo_secondary as ds
>>> ds.clean_slate(partial_verifying=True)
>>> ds.update_cycle()
```

The same optional arguments apply for the demo partial-verification Secondary
as for the full-verification version [above](#window-3-the-secondary-clients)
(ECU id/serial, vehicle id/VIN, Primary port, etc.).


The Secondary's update_cycle() call does the following:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it help readability if the parts of the update_cycle() that differ between full-verification and partial-verification stand out (perhaps in bold)?

- fetches the signed Director's Targets role metadata for the vehicle from the
Primary
- checks for a valid signature over the Targets role metadata, from the key it
knows to be the Director's Targets key.
- looks for target info (hash, length, etc.) in the now-validated Targets
metadata that includes this Secondary's ECU identifier, indicating an
instruction from the Director to install that target firmware
- fetches the image the Primary assigns to this ECU
- validates that image against the target info from the validated Targets
metadata. If the image from the Primary does not match validated metadata, it is discarded.
- 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, the signature is correct and by the expected key, and the time
is more recent than the last validated time, this time is saved as valid,
allowing this Secondary to disregard any metadata with an expiration date
earlier than that time.
- 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 run directly: `ds.generate_signed_ecu_manifest()`)
- submits the ECU Version Manifest to the Primary (can also be run directly: `ds.submit_ecu_manifest_to_primary()`)





# Testing

Expand Down
51 changes: 35 additions & 16 deletions demo/demo_secondary.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@ def clean_slate(
vin=_vin,
ecu_serial=_ecu_serial,
primary_host=None,
primary_port=None):
primary_port=None,
partial_verifying=False):
"""
"""

Expand All @@ -91,7 +92,6 @@ def clean_slate(
global nonce
global CLIENT_DIRECTORY
global attacks_detected

_vin = vin
_ecu_serial = ecu_serial

Expand Down Expand Up @@ -142,6 +142,11 @@ def clean_slate(
tuf.conf.repository_directory = CLIENT_DIRECTORY # This setting should probably be called CLIENT_DIRECTORY instead, post-TAP4.


#Importing director's public key if secondary is partial verification
if partial_verifying:
key_director_pub = demo.import_public_key('director')
else:
key_director_pub = None

# Initialize a full verification Secondary ECU.
# This also generates a nonce to use in the next time query, sets the initial
Expand All @@ -154,7 +159,9 @@ def clean_slate(
ecu_key=ecu_key,
time=clock,
firmware_fileinfo=factory_firmware_fileinfo,
timeserver_public_key=key_timeserver_pub)
timeserver_public_key=key_timeserver_pub,
director_public_key = key_director_pub,
partial_verifying = partial_verifying)



Expand Down Expand Up @@ -300,9 +307,14 @@ def update_cycle():
# from it like so:
time_attestation = time_attestation.data

# Download the metadata from the Primary in the form of an archive. This
# returns the binary data that we need to write to file.
metadata_archive = pserver.get_metadata(secondary_ecu.ecu_serial)
# Obtain metadata from the Primary, either as a single role file (the
# Director Targets role file) if this is a partial-verification Secondary,
# or as an archive that includes all the metadata files if this is a full-
# verification Secondary.
# This call returns the binary data that we need to write to the file.

metadata_from_primary = pserver.get_metadata(
secondary_ecu.ecu_serial, secondary_ecu.partial_verifying)

# Validate the time attestation and internalize the time. Continue
# regardless.
Expand All @@ -320,16 +332,23 @@ def update_cycle():
#else:
# print(GREEN + 'Official time has been updated successfully.' + ENDCOLORS)

# Dump the archive file to disk.
archive_fname = os.path.join(
secondary_ecu.full_client_dir, 'metadata_archive.zip')

with open(archive_fname, 'wb') as fobj:
fobj.write(metadata_archive.data)

# Now tell the Secondary reference implementation code where the archive file
# is and let it expand and validate the metadata.
secondary_ecu.process_metadata(archive_fname)
# Write the metadata retrieved from the Primary to disk, whether it is a
# single role file (partial verification) or the full metadata archive.
if secondary_ecu.partial_verifying:
director_targets_role = os.path.join(
secondary_ecu.full_client_dir, 'director_targets.'+tuf.conf.METADATA_FORMAT)
with open(director_targets_role, 'wb') as f:
f.write(metadata_from_primary.data)
secondary_ecu.process_metadata(director_targets_role)
else:
archive_fname = os.path.join(
secondary_ecu.full_client_dir, 'metadata_from_primary.zip')
with open(archive_fname, 'wb') as fobj:
fobj.write(metadata_from_primary.data)

# Now tell the Secondary reference implementation code where the archive
# file is and let it expand (as necessary) and validate the metadata.
secondary_ecu.process_metadata(archive_fname)


# As part of the process_metadata call, the secondary will have saved
Expand Down
Binary file added samples/director_targets_bad_sig_v2.der
Binary file not shown.
30 changes: 30 additions & 0 deletions samples/director_targets_bad_sig_v2.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"signatures": [
{
"keyid": "630cf584f392430b2119a4395e39624e86f5e5c5374507a789be5cf35bf090d6",
"method": "ed25519",
"sig": "a560312be8ebd451c689724a3ec2fcbdb7ba708b27669861e0146bd61a2a15f04baf90edc75edd2e437ac680a5d2b3d76b3e4b9766a8a961108af6b4e501950e"
}
],
"signed": {
"_type": "Targets",
"delegations": {
"keys": {},
"roles": []
},
"expires": "2021-01-10T15:14:21Z",
"targets": {
"/firmware13.img": {
"custom": {
"ecu_serial": "20000"
},
"hashes": {
"sha256": "daeec2555599b8e7a82b6f1339d5f419346b57a0eb7a39ee6334b8f205595752",
"sha512": "1570937a84e9e74e35f5e56a8f8518c91e18258cffc8ace249d9acf173d1845d82002583b1b53373b79edf52494d524f2619f5f1896f3085038deca92c950486"
},
"length": 20
}
},
"version": 2
}
}
Binary file added samples/director_targets_empty_v1.der
Binary file not shown.
19 changes: 19 additions & 0 deletions samples/director_targets_empty_v1.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"signatures": [
{
"keyid": "630cf584f392430b2119a4395e39624e86f5e5c5374507a789be5cf35bf090d6",
"method": "ed25519",
"sig": "f474702d61728fbfa01cc6d9b6e9b6681710680927efc2cdcbae1e758b89ddb355c8ce2c636ab26160040c9a575265b81311a4e40349463f34e784b48d4e850e"
}
],
"signed": {
"_type": "Targets",
"delegations": {
"keys": {},
"roles": []
},
"expires": "2031-10-21T18:00:34Z",
"targets": {},
"version": 1
}
}
Binary file added samples/director_targets_empty_v2.der
Binary file not shown.
19 changes: 19 additions & 0 deletions samples/director_targets_empty_v2.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"signatures": [
{
"keyid": "630cf584f392430b2119a4395e39624e86f5e5c5374507a789be5cf35bf090d6",
"method": "ed25519",
"sig": "3b6f11eb9382fdbdbbcce54641617d8ea159d065f70484a3b013641ef22619902b252692286f6741e1fd9d68b3805dc727136b0b3ccc29c51b4bc97ecaf2f708"
}
],
"signed": {
"_type": "Targets",
"delegations": {
"keys": {},
"roles": []
},
"expires": "2031-10-21T18:00:34Z",
"targets": {},
"version": 2
}
}
Binary file added samples/director_targets_empty_v3.der
Binary file not shown.
19 changes: 19 additions & 0 deletions samples/director_targets_empty_v3.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"signatures": [
{
"keyid": "630cf584f392430b2119a4395e39624e86f5e5c5374507a789be5cf35bf090d6",
"method": "ed25519",
"sig": "a36a0816ffe796a451d431a4f9b9f5df350650c69c36bf22397631c4418f6778f5da01e302d19e565638616cb0c32dcc3caf3cc0e92741a0a284a8b9cf62480a"
}
],
"signed": {
"_type": "Targets",
"delegations": {
"keys": {},
"roles": []
},
"expires": "2031-10-21T18:00:34Z",
"targets": {},
"version": 3
}
}
Binary file added samples/director_targets_expired_v1.der
Binary file not shown.
19 changes: 19 additions & 0 deletions samples/director_targets_expired_v1.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"signatures": [
{
"keyid": "630cf584f392430b2119a4395e39624e86f5e5c5374507a789be5cf35bf090d6",
"method": "ed25519",
"sig": "fb26d9d8f9314175cc7d6cfdcc0628a57f24f9ef43b84a34496b06c7e53864f6581e02a8589fc307603e9de0275ba0edce8044a010666f7f72ca70c423b4e10d"
}
],
"signed": {
"_type": "Targets",
"delegations": {
"keys": {},
"roles": []
},
"expires": "2018-02-21T16:14:48Z",
"targets": {},
"version": 1
}
}
Binary file added samples/director_targets_expired_v2.der
Binary file not shown.
19 changes: 19 additions & 0 deletions samples/director_targets_expired_v2.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"signatures": [
{
"keyid": "630cf584f392430b2119a4395e39624e86f5e5c5374507a789be5cf35bf090d6",
"method": "ed25519",
"sig": "2f43ed700ee2205de1ffea87b7008ecb1bac8777895f8565cfd23c3a8b32fd9ac882f5e94b772ff0e9ad10c49f6be14cba65b39c95eeae944fa5831d6de23a0e"
}
],
"signed": {
"_type": "Targets",
"delegations": {
"keys": {},
"roles": []
},
"expires": "2018-02-21T17:36:11Z",
"targets": {},
"version": 2
}
}
Binary file added samples/director_targets_expired_v3.der
Binary file not shown.
19 changes: 19 additions & 0 deletions samples/director_targets_expired_v3.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"signatures": [
{
"keyid": "630cf584f392430b2119a4395e39624e86f5e5c5374507a789be5cf35bf090d6",
"method": "ed25519",
"sig": "41b105b7af297ce24c58971af31df3cb80d28a5464eeb7424b3c9e57f4e955aa9f688eed0dd0399b82b888ea52cc4309d8380bd7f37aeae42778741686d45f03"
}
],
"signed": {
"_type": "Targets",
"delegations": {
"keys": {},
"roles": []
},
"expires": "2018-02-21T17:36:11Z",
"targets": {},
"version": 3
}
}
Binary file added samples/director_targets_pv_bcu_v2.der
Binary file not shown.
30 changes: 30 additions & 0 deletions samples/director_targets_pv_bcu_v2.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"signatures": [
{
"keyid": "630cf584f392430b2119a4395e39624e86f5e5c5374507a789be5cf35bf090d6",
"method": "ed25519",
"sig": "22823ee73a94b427860247b964a1fcfba15f23cdd294919e6e5687b293eccfc210be49a362299b5a8157b5601fa1a7dc41d3f82b3a026804b15ebfee3e420d0a"
}
],
"signed": {
"_type": "Targets",
"delegations": {
"keys": {},
"roles": []
},
"expires": "2031-10-22T19:07:35Z",
"targets": {
"/BCU1.0.txt": {
"custom": {
"ecu_serial": "pv_bcu"
},
"hashes": {
"sha256": "fb0aa5699a4e7b68009fed6b094ecb00c3ad5670921be1b902b72a23cd4675b1",
"sha512": "0b0bb00bccf7bdad519d0a0af2794c945bd51ebdbc79f9616f0e3903b32f4ce2d5b250ab1bc2d34194bacf720b4f0aed361ef8d59ac72b1bc19e3a223a5e87cd"
},
"length": 15
}
},
"version": 2
}
}
Loading