Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parse example SUIT Manifest files #62

Merged
Merged
50 changes: 49 additions & 1 deletion .github/workflows/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: Validating SUIT Encrypted Payloads examples
on: pull_request

jobs:
test:
cddl-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
Expand All @@ -17,3 +17,51 @@ jobs:
- run: sudo apt-get update && sudo apt-get install -y xxd
- name: Vaildate examples
run: make -C examples/ validate
- name: Upload *.suit files
uses: actions/upload-artifact@v4
with:
name: suit-encrypted-payloads
path: |
./examples/suit-manifest-aes-kw-content.suit
./examples/suit-manifest-aes-kw.suit
./examples/suit-manifest-aes-kw-slot.suit
./examples/suit-manifest-es-ecdh-content.suit
./examples/suit-manifest-es-ecdh-dependency.suit
parse-test:
runs-on: ubuntu-latest
needs: cddl-test
steps:
- name: Download *.suit files
uses: actions/download-artifact@v4
with:
name: suit-encrypted-payloads
- name: Build parsers
run: |
sudo apt-get install -y git make gcc libssl-dev
git clone --depth 1 https://github.com/laurencelundblade/QCBOR.git
sudo make -C QCBOR install
git clone --depth 1 --branch dev https://github.com/laurencelundblade/t_cose.git
sudo make -C t_cose -f Makefile.ossl install
git clone --depth 1 https://github.com/kentakayama/libcsuit.git
make -C libcsuit -f Makefile.parser
make -C libcsuit -f Makefile.process
- name: Parse suit-manifest-aes-kw-content.suit
run: ./libcsuit/bin/suit_manifest_parser ./suit-manifest-aes-kw-content.suit
- name: Process suit-manifest-aes-kw-content.suit
run: ./libcsuit/bin/suit_manifest_process ./suit-manifest-aes-kw-content.suit
- name: Parse suit-manifest-aes-kw.suit
run: ./libcsuit/bin/suit_manifest_parser ./suit-manifest-aes-kw.suit
- name: Process suit-manifest-aes-kw.suit
run: ./libcsuit/bin/suit_manifest_process ./suit-manifest-aes-kw.suit
- name: Parse suit-manifest-aes-kw-slot.suit
run: ./libcsuit/bin/suit_manifest_parser ./suit-manifest-aes-kw-slot.suit
- name: Process suit-manifest-aes-kw-slot.suit
run: ./libcsuit/bin/suit_manifest_process ./suit-manifest-aes-kw-slot.suit
- name: Parse suit-manifest-es-ecdh-content.suit
run: ./libcsuit/bin/suit_manifest_parser ./suit-manifest-es-ecdh-content.suit
- name: Process suit-manifest-es-ecdh-content.suit
run: ./libcsuit/bin/suit_manifest_process ./suit-manifest-es-ecdh-content.suit
- name: Parse suit-manifest-es-ecdh-dependency.suit
run: ./libcsuit/bin/suit_manifest_parser ./suit-manifest-es-ecdh-dependency.suit
- name: Process suit-manifest-es-ecdh-dependency.suit
run: ./libcsuit/bin/suit_manifest_process ./suit-manifest-es-ecdh-dependency.suit
103 changes: 13 additions & 90 deletions draft-ietf-suit-firmware-encryption.md
Original file line number Diff line number Diff line change
Expand Up @@ -750,7 +750,7 @@ This example uses the following parameters:
- d: h'60FE6DD6D85D5740A5349B6F91267EEAC5BA81B8CB53EE249E4B4EB102C476B3'
- kid: 'kid-2'
- KDF Context
- Algorithm ID: 1 (A128GCM)
- Algorithm ID: -3 (A128KW)
- SuppPubInfo
- keyDataLength: 128
- protected: { / alg / 1: -29 / ECDH-ES+A128KW / }
Expand Down Expand Up @@ -863,7 +863,7 @@ This example uses the following parameters:
- d: h'60FE6DD6D85D5740A5349B6F91267EEAC5BA81B8CB53EE249E4B4EB102C476B3'
- kid: 'kid-2'
- KDF Context
- Algorithm ID: -65534 (A128CTR)
- Algorithm ID: -3 (A128KW)
- SuppPubInfo
- keyDataLength: 128
- protected: { / alg / 1: -29 / ECDH-ES+A128KW / }
Expand Down Expand Up @@ -983,7 +983,7 @@ This example uses the following parameters:
- d: h'60FE6DD6D85D5740A5349B6F91267EEAC5BA81B8CB53EE249E4B4EB102C476B3'
- kid: 'kid-2'
- KDF Context
- Algorithm ID: -65531 (A128CBC)
- Algorithm ID: -3 (A128KW)
- SuppPubInfo
- keyDataLength: 128
- protected: { / alg / 1: -29 / ECDH-ES+A128KW / }
Expand Down Expand Up @@ -1324,9 +1324,7 @@ another component with the suit-directive-copy directive. This approach
works well on constrained devices with XIP flash memory.

The SUIT manifest in diagnostic notation (with line breaks added
for readability) is shown below. Line numbers and additional notes
(see /!!! text !!!/ ) have been inserted to provide further
information about the manifest processing.
for readability) is shown below.

~~~
{::include examples/suit-manifest-aes-kw.diag.signed}
Expand All @@ -1336,39 +1334,35 @@ The default storage area is defined by the component identifier (see Section 8.4

While parsing the manifest, the behavior of SUIT manifest processor would be

- [L1-L17] authenticates the manifest part on [L18-L65]
- [L2-L17] authenticates the manifest part on [L18-L68]
- [L22-L25] gets two component identifiers; ['plaintext-firmware'] for component #0, and ['encrypted-firmware'] for component # 1 respectively
- [L29] sets current component index # 1 (the lasting directives target ['encrypted-firmware'])
- [L32] sets source uri parameter "https://example.com/encrypted-firmware"
- [L34] fetches content from source uri into ['encrypted-firmware']
- [L37] sets current component index # 0 (the lasting directives target ['plaintext-firmware'])
- [L39-L60] sets SUIT encryption info parameter
- [L61] sets source component index parameter # 1
- [L63] decrypts component # 1 (source component index) and stores the result into component # 0 (current component index)
- [L33] sets source uri parameter "https://example.com/encrypted-firmware"
- [L35] fetches content from source uri into ['encrypted-firmware']
- [L38] sets current component index # 0 (the lasting directives target ['plaintext-firmware'])
- [L41-L61] sets SUIT encryption info parameter
- [L62] sets source component index parameter # 1
- [L65] decrypts component # 1 (source component index) and stores the result into component # 0 (current component index)

The following attributes and features from the SUIT manifest specification are used:

~~~
| Attribute Name | Abbreviation | Manifest Reference |
|--------------------------------------------|---------------|--------------------|
| component identifier | CI | Section 8.4.5.1 |
| (destination) component index | dst-CI | Section 8.4.10.1 |
| (destination) component slot OPTIONAL param| dst-CS | Section 8.4.8.8 |
| (source) uri OPTIONAL parameter | src-URI | Section 8.4.8.10 |
| source component index OPTIONAL parameter | src-CI | Section 8.4.8.11 |
~~~

The resulting state of SUIT manifest processor is shown in the following table:

~~~
| Abbreviation | Plaintext | Ciphertext |
|---------------|------------------------|------------------------------------------|
| CI | ['plaintext-firmware'] | ['encrypted-firmware'] |
| dst-CI | 0 | 1 |
| dst-CS | N/A | N/A |
| src-URI | N/A | "https://example.com/encrypted-firmware" |
| src-CI | 1 | N/A |
~~~

In hex format, the SUIT manifest shown above is:

Expand All @@ -1379,79 +1373,7 @@ In hex format, the SUIT manifest shown above is:
The example above does not use storage slots. However, it is possible to specify this functionality for devices that support slots in flash memory. In the augmented example below we refer to the slots using [h'00'] and [h'01']. The component identifier [h'00'] would, in this example, specify the component slot #0.

~~~
1 / SUIT_Envelope_Tagged / 107({
2 / authentication-wrapper / 2: << [
3 << [
4 / digest-algorithm-id: / -16 / SHA256 /,
5 / digest-bytes: / h'AAB6A7868C4E43D5983BDE019EF22779
6 21F6F8EF1FCAF9403CA97255BED2CD30'
7 ] >>,
8 << / COSE_Mac0_Tagged / 17([
9 / protected: / << {
10 / algorithm-id / 1: 5 / HMAC256 /
11 } >>,
12 / unprotected: / {},
13 / payload: / null,
14 / tag: / h'93B4B774A5D0421ED6FB5EBF890A284C
15 DAC7816CBC048BF47EE7FA7FF3BC02C3'
16 ]) >>
17 ] >>,
18 / manifest / 3: << {
19 / manifest-version / 1: 1,
20 / manifest-sequence-number / 2: 1,
21 / common / 3: << {
22 / components / 2: [
* /!!! component identifier for component index #0 !!!/
23 [h'00'],
* /!!! component identifier for component index #1 !!!/
24 [h'01']
25 ]
26 } >>,
27 / install / 17: << [
28 / fetch encrypted firmware /
* /!!! destination component index #1 = [h'01'] !!!/
29 / directive-set-component-index / 12, 1,
30 / directive-override-parameters / 20, {
31 / parameter-image-size / 14: 46,
* /!!! source uri of #1 !!!/
32 / parameter-uri / 21: "https://example.com/encrypted-firmware"
33 },
34 / directive-fetch / 21, 15,
35
36 / decrypt encrypted firmware /
* /!!! destination component index #0 = [h'00'] !!!/
37 / directive-set-component-index / 12, 0,
38 / directive-override-parameters / 20, {
39 / parameter-encryption-info / 19: << 96([
40 / protected: / << {
41 / alg / 1: 1 / AES-GCM-128 /
42 } >>,
43 / unprotected: / {
44 / IV / 5: h'F14AAB9D81D51F7AD943FE87AF4F70CD'
45 },
46 / payload: / null / detached ciphertext /,
47 / recipients: / [
48 [
49 / protected: / << {
50 } >>,
51 / unprotected: / {
52 / alg / 1: -3 / A128KW /,
53 / kid / 4: 'kid-1'
54 },
55 / payload: /
56 h'75603FFC9518D794713C8CA8A115A7FB32565A6D59534D62'
57 / CEK encrypted with KEK /
58 ]
59 ]
60 ]) >>,
* /!!! source component index #1 = [h'01'] !!!/
61 / parameter-source-component / 22: 1
62 },
* /!!! consumes the SUIT_Encryption_Info above !!!/
63 / directive-copy / 22, 15
64 ] >>
65 } >>
66 })
{::include examples/suit-manifest-aes-kw-slot.diag.signed}
~~~

## ES-DH Example with Write + Copy Directives {#example-ES-DH-write}
Expand Down Expand Up @@ -1480,6 +1402,7 @@ In hex format, the SUIT manifest is this:
The following SUIT manifest requests a parser to resolve the dependency.

The dependent manifest is signed with another key:

~~~
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIIQa67e56m8CYL5zVaJFiLl30j0qxb8ray2DeUMqH+qYoAoGCCqGSM49
Expand Down
11 changes: 7 additions & 4 deletions examples/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ SUIT_ENCRYPTION_INFO := \
suit-encryption-info-es-ecdh-aes-ctr.cose

SUIT_MANIFEST_WITH_ENCRYPTED_PAYLOAD := \
suit-manifest-aes-kw.suit \
suit-manifest-aes-kw-content.suit \
suit-manifest-aes-kw.suit \
suit-manifest-aes-kw-slot.suit \
suit-manifest-es-ecdh-content.suit \
suit-manifest-es-ecdh-dependency.suit

Expand Down Expand Up @@ -55,6 +56,7 @@ draft-ietf-suit-trust-domains.cddl:

draft-ietf-suit-manifest.cddl: draft-ietf-suit-firmware-encryption.cddl draft-ietf-suit-trust-domains.cddl
curl --retry 3 https://raw.githubusercontent.com/suit-wg/manifest-spec/master/draft-ietf-suit-manifest.cddl -o $@
sed -i -e 's/suit-install = 17/suit-install = 20/' $@
cat draft-ietf-suit-firmware-encryption.cddl >> $@
cat draft-ietf-suit-trust-domains.cddl >> $@

Expand Down Expand Up @@ -92,8 +94,9 @@ validate_cddl_match: all cddl
RUBYOPT="-W0" cddl draft-ietf-suit-firmware-encryption.cddl validate suit-encryption-info-es-ecdh-aes-ctr.cose
RUBYOPT="-W0" cddl esdh_aeskw.cddl validate suit-encryption-info-es-ecdh-aes-ctr.cose
@echo [SUCCESS] Each SUIT_Encryption_Info matches to its CDDL
RUBYOPT="-W0" cddl draft-ietf-suit-manifest.cddl validate suit-manifest-aes-kw.suit
RUBYOPT="-W0" cddl draft-ietf-suit-manifest.cddl validate suit-manifest-aes-kw-content.suit
RUBYOPT="-W0" cddl draft-ietf-suit-manifest.cddl validate suit-manifest-aes-kw.suit
RUBYOPT="-W0" cddl draft-ietf-suit-manifest.cddl validate suit-manifest-aes-kw-slot.suit
RUBYOPT="-W0" cddl draft-ietf-suit-manifest.cddl validate suit-manifest-es-ecdh-content.suit
RUBYOPT="-W0" cddl draft-ietf-suit-manifest.cddl validate suit-manifest-es-ecdh-dependency.suit
@echo [SUCCESS] Each SUIT Manifest with Encrypted Payloads matches to its CDDL
Expand All @@ -111,9 +114,9 @@ validate_decrypted_plaintext:
@echo "\n# ECDH-ES+AES-KW + A128GCM (Example 2)"
python3 ./validate_esdh_aead_suit_encryption_info.py suit-encryption-info-es-ecdh-aes-gcm.hex encrypted-payload-es-ecdh-aes-gcm.hex
@echo "\n# ECDH-ES+AES-KW + A128CTR"
python3 ./validate_esdh_non_aead_suit_encryption_info.py suit-encryption-info-es-ecdh-aes-ctr.hex encrypted-payload-es-ecdh-aes-ctr.hex A128CTR
python3 ./validate_esdh_non_aead_suit_encryption_info.py suit-encryption-info-es-ecdh-aes-ctr.hex encrypted-payload-es-ecdh-aes-ctr.hex
@echo "\n# ECDH-ES+AES-KW + A128CBC"
python3 ./validate_esdh_non_aead_suit_encryption_info.py suit-encryption-info-es-ecdh-aes-cbc.hex encrypted-payload-es-ecdh-aes-cbc.hex A128CBC
python3 ./validate_esdh_non_aead_suit_encryption_info.py suit-encryption-info-es-ecdh-aes-cbc.hex encrypted-payload-es-ecdh-aes-cbc.hex
@echo [SUCCESS] The expected plaintext firmware can be decrypted

.PHONY: clean
Expand Down
4 changes: 2 additions & 2 deletions examples/encrypted-payload-aes-kw-aes-gcm.hex
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
2F59C3A34D9570FB99A5382E66466A3221A8AD85CE508BA306FB431A60EF
A5AAAA078355070205A4B196832DF17F
758C4B7BBAE2C4C1D462423E0F0DC3164FFA7B85BB94D4BD6D7ED26AB32F
EB063385D4D3465927EC82CB5E198A59
4 changes: 2 additions & 2 deletions examples/encrypted-payload-es-ecdh-aes-gcm.hex
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
2F59C3A34D9570FB99A5382E66466A3221A8AD85CE508BA306FB431A60EF
A5AAAA078355070205A4B196832DF17F
758C4B7BBAE2C4C1D462423E0F0DC3164FFA7B85BB94D4BD6D7ED26AB32F
EB063385D4D3465927EC82CB5E198A59
4 changes: 2 additions & 2 deletions examples/generate_suit_encryption_info_aescbc_aesctr.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@
receiver_public_key_jwk[key] = base64.b64encode(bytes.fromhex(receiver_public_key_jwk[key])).decode()

kdf_context_a128cbc = {
"alg": "A128CBC",
"alg": "A128KW",
"supp_pub": {
"key_data_length": 128,
"protected": {"alg": "ECDH-ES+A128KW"},
Expand Down Expand Up @@ -212,7 +212,7 @@
receiver_public_key_jwk[key] = base64.b64encode(bytes.fromhex(receiver_public_key_jwk[key])).decode()

kdf_context_a128ctr = {
"alg": "A128CTR",
"alg": "A128KW",
"supp_pub": {
"key_data_length": 128,
"protected": {"alg": "ECDH-ES+A128KW"},
Expand Down
6 changes: 3 additions & 3 deletions examples/generate_suit_encryption_info_aesgcm.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"alg": "A128GCM"
},
unprotected={
"iv": bytes.fromhex("F14AAB9D81D51F7AD943FE87AF4F70CD")
"iv": bytes.fromhex("F14AAB9D81D51F7AD943FE87")
},
recipients=[r],
)
Expand Down Expand Up @@ -84,7 +84,7 @@
receiver_public_key_jwk[key] = base64.b64encode(bytes.fromhex(receiver_public_key_jwk[key])).decode()

kdf_context_a128gcm = {
"alg": "A128GCM",
"alg": "A128KW",
"supp_pub": {
"key_data_length": 128,
"protected": {"alg": "ECDH-ES+A128KW"},
Expand All @@ -108,7 +108,7 @@
"alg": "A128GCM"
},
unprotected={
"iv": bytes.fromhex("F14AAB9D81D51F7AD943FE87AF4F70CD")
"iv": bytes.fromhex("F14AAB9D81D51F7AD943FE87")
},
recipients=[r],
)
Expand Down
4 changes: 2 additions & 2 deletions examples/suit-encryption-info-aes-kw-aes-gcm.diag
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
96([
/ protected: / << {
/ alg / 1: 1 / AES-GCM-128 /
/ alg / 1: 1 / A128GCM /
} >>,
/ unprotected: / {
/ IV / 5: h'F14AAB9D81D51F7AD943FE87AF4F70CD'
/ IV / 5: h'F14AAB9D81D51F7AD943FE87'
},
/ payload: / null / detached ciphertext /,
/ recipients: / [
Expand Down
6 changes: 3 additions & 3 deletions examples/suit-encryption-info-aes-kw-aes-gcm.hex
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
D8608443A10101A10550F14AAB9D81D51F7AD943FE87AF4F70CDF6818340
A2012204456B69642D31581875603FFC9518D794713C8CA8A115A7FB3256
5A6D59534D62
D8608443A10101A1054CF14AAB9D81D51F7AD943FE87F6818340A2012204
456B69642D31581875603FFC9518D794713C8CA8A115A7FB32565A6D5953
4D62
8 changes: 5 additions & 3 deletions examples/suit-encryption-info-es-ecdh-aes-cbc.diag
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@
/ ephemeral key / -1: {
/ kty / 1: 2 / EC2 /,
/ crv / -1: 1 / P-256 /,
/ x / -2: h'BC6A2DCD5025C8C0F7A5D120EB3E458CA722F8FB94BD56A24709CB15A8697489',
/ y / -3: h'10136574F673511540FE2A8589A7EDA372CB7B1AF94A8E1B4B94F6BDBD98AA18'
/ x / -2: h'84C1768A1AFA92BAF27C3289A3FAF75E
7511B27C654947BBD391E8A12EB8295B',
/ y / -3: h'F32B0AECA4F8B6C51C155037B1FB726C
AE3D0C77976864EF725DB2B30ABCE6F6'
}
},
/ payload: / h'AC8CDFB5426422298FCF235EB5F24D9E4C44C1689167473A'
/ payload: / h'04928D52CD34AC8CA0427CEED6D7C5D06A21B5F4F79CAA18'
/ CEK encrypted with KEK /
]
]
Expand Down
8 changes: 4 additions & 4 deletions examples/suit-encryption-info-es-ecdh-aes-cbc.hex
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
D8608440A20139FFFA055093702C81590F845D9EC866CCAC767BD1F68183
44A101381CA120A401022001215820BC6A2DCD5025C8C0F7A5D120EB3E45
8CA722F8FB94BD56A24709CB15A869748922582010136574F673511540FE
2A8589A7EDA372CB7B1AF94A8E1B4B94F6BDBD98AA185818AC8CDFB54264
22298FCF235EB5F24D9E4C44C1689167473A
44A101381CA120A40102200121582084C1768A1AFA92BAF27C3289A3FAF7
5E7511B27C654947BBD391E8A12EB8295B225820F32B0AECA4F8B6C51C15
5037B1FB726CAE3D0C77976864EF725DB2B30ABCE6F6581804928D52CD34
AC8CA0427CEED6D7C5D06A21B5F4F79CAA18
8 changes: 5 additions & 3 deletions examples/suit-encryption-info-es-ecdh-aes-ctr.diag
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@
/ ephemeral key / -1: {
/ kty / 1: 2 / EC2 /,
/ crv / -1: 1 / P-256 /,
/ x / -2: h'50364E4DF3F5E8749D98E4378C04FAFE643B6ACEE7138382D83F768C7186FB85',
/ y / -3: h'99E6C96BEF3952B12EF83921B1749475D767284AA42D74D8923C137B01EDF5A0'
/ x / -2: h'EE0718F6B019C29CC611C18CEDE22140
66DDCEDC2F0DBEF873CB224C715C1174',
/ y / -3: h'279F2A88E4AB9E2ED30C0FCB69515B31
B5D36725BFDB9AE02032ED4D5AB52CB8'
}
},
/ payload: / h'E8599DCEE4944EECA9781D3ECDE3D9C34E1C9FCE8906617F'
/ payload: / h'E28B4502E4F5151884A995405579006E9465C3E94E3E0808'
/ CEK encrypted with KEK /
]
]
Expand Down
Loading
Loading