1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2025-01-18 12:52:44 +01:00

suit: remove moran-v4 version of the parser

Co-authored-by: Kaspar Schleiser <kaspar@schleiser.de>
This commit is contained in:
Koen Zandberg 2020-02-26 14:23:03 +01:00
parent dde05355c5
commit ef8daaf7be
No known key found for this signature in database
GPG Key ID: 0E63411F8FCA8247
13 changed files with 0 additions and 1917 deletions

View File

@ -1,33 +0,0 @@
#!/usr/bin/env python3
#
# Copyright (C) 2019 Inria
# 2019 FU Berlin
#
# This file is subject to the terms and conditions of the GNU Lesser
# General Public License v2.1. See the file LICENSE in the top level
# directory for more details.
#
import sys
import ed25519
def main():
if len(sys.argv) != 3:
print("usage: gen_key.py <secret filename> <public filename>")
sys.exit(1)
_signing_key, _verifying_key = ed25519.create_keypair()
with open(sys.argv[1], "wb") as f:
f.write(_signing_key.to_bytes())
with open(sys.argv[2], "wb") as f:
f.write(_verifying_key.to_bytes())
vkey_hex = _verifying_key.to_ascii(encoding="hex")
print("Generated public key: '{}'".format(vkey_hex.decode()))
if __name__ == '__main__':
main()

View File

@ -1,94 +0,0 @@
#!/usr/bin/env python3
#
# Copyright (C) 2019 Inria
# 2019 FU Berlin
#
# This file is subject to the terms and conditions of the GNU Lesser
# General Public License v2.1. See the file LICENSE in the top level
# directory for more details.
#
import os
import hashlib
import json
import uuid
import argparse
from suit_manifest_encoder_04 import compile_to_suit
def str2int(x):
if x.startswith("0x"):
return int(x, 16)
else:
return int(x)
def sha256_from_file(filepath):
sha256 = hashlib.sha256()
sha256.update(open(filepath, "rb").read())
return sha256.digest()
def parse_arguments():
parser = argparse.ArgumentParser(
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument('--template', '-t', help='Manifest template file path')
parser.add_argument('--urlroot', '-u', help='')
parser.add_argument('--offsets', '-O', help='')
parser.add_argument('--seqnr', '-s',
help='Sequence number of the manifest')
parser.add_argument('--output', '-o', nargs='?',
help='Manifest output binary file path')
parser.add_argument('--uuid-vendor', '-V',
help='Manifest vendor uuid')
parser.add_argument('--uuid-class', '-C',
help='Manifest class uuid')
parser.add_argument('slotfiles', nargs=2,
help='The list of slot file paths')
return parser.parse_args()
def main(args):
uuid_vendor = uuid.uuid5(uuid.NAMESPACE_DNS, args.uuid_vendor)
uuid_class = uuid.uuid5(uuid_vendor, args.uuid_class)
with open(args.template, 'r') as f:
template = json.load(f)
template["sequence-number"] = int(args.seqnr)
template["conditions"] = [
{"condition-vendor-id": uuid_vendor.hex},
{"condition-class-id": uuid_class.hex},
]
offsets = [str2int(offset) for offset in args.offsets.split(",")]
for slot, slotfile in enumerate(args.slotfiles):
filename = slotfile
size = os.path.getsize(filename)
uri = os.path.join(args.urlroot, os.path.basename(filename))
offset = offsets[slot]
_image_slot = template["components"][0]["images"][slot]
_image_slot.update({
"file": filename,
"uri": uri,
"size": size,
"digest": sha256_from_file(slotfile),
})
_image_slot["conditions"][0]["condition-component-offset"] = offset
_image_slot["file"] = filename
result = compile_to_suit(template)
if args.output is not None:
with open(args.output, 'wb') as f:
f.write(result)
else:
print(result)
if __name__ == "__main__":
_args = parse_arguments()
main(_args)

View File

@ -1,154 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# ----------------------------------------------------------------------------
# Copyright 2018-2019 ARM Limited or its affiliates
#
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ----------------------------------------------------------------------------
"""
This is a demo script that is intended to act as a reference for SUIT manifest
signing.
NOTE: It is expected that C and C++ parser implementations will be written
against this script, so it does not adhere to PEP8 in order to maintain
similarity between the naming in this script and that of C/C++ implementations.
"""
import sys
import copy
import cbor
import ed25519
from pprint import pprint
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives import serialization
# Private key in arg 1
# Public key in arg 2
# Input file in arg 3
# Output file in arg 4
COSE_Sign_Tag = 98
APPLICATION_OCTET_STREAM_ID = 42
ES256 = -7
EDDSA = -8
def signWrapper(algo, private_key, public_key, encwrapper):
wrapper = cbor.loads(encwrapper)
pprint(wrapper[1])
COSE_Sign = copy.deepcopy(wrapper[1])
if not COSE_Sign:
protected = cbor.dumps({
3: APPLICATION_OCTET_STREAM_ID, # Content Type
})
unprotected = {
}
signatures = []
# Create a COSE_Sign_Tagged object
COSE_Sign = [
protected,
unprotected,
b'',
signatures
]
if algo == EDDSA:
public_bytes = public_key.to_bytes()
else:
public_bytes = public_key.public_bytes(
serialization.Encoding.DER,
serialization.PublicFormat.SubjectPublicKeyInfo)
# NOTE: Using RFC7093, Method 4
digest = hashes.Hash(hashes.SHA256(), backend=default_backend())
digest.update(public_bytes)
kid = digest.finalize()
# Sign the payload
protected = cbor.dumps({
1: algo, # alg
})
# Create the signing object
unprotected = {
4: kid # kid
}
Sig_structure = [
"Signature", # Context
COSE_Sign[0], # Body Protected
protected, # signature protected
b'', # External AAD
wrapper[2] # payload
]
sig_str = cbor.dumps(Sig_structure, sort_keys=True)
if algo == EDDSA:
signature = private_key.sign(sig_str)
else:
signature = private_key.sign(
sig_str,
ec.ECDSA(hashes.SHA256())
)
COSE_Signature = [
protected,
unprotected,
signature
]
COSE_Sign[3].append(COSE_Signature)
wrapper[1] = cbor.dumps(cbor.Tag(COSE_Sign_Tag, COSE_Sign), sort_keys=True)
return wrapper
def main():
private_key = None
algo = ES256
with open(sys.argv[1], 'rb') as fd:
priv_key_bytes = fd.read()
try:
private_key = serialization.load_pem_private_key(
priv_key_bytes, password=None, backend=default_backend())
except ValueError:
algo = EDDSA
private_key = ed25519.SigningKey(priv_key_bytes)
public_key = None
with open(sys.argv[2], 'rb') as fd:
pub_key_bytes = fd.read()
try:
public_key = serialization.load_pem_public_key(
pub_key_bytes, backend=default_backend())
except ValueError:
public_key = ed25519.VerifyingKey(pub_key_bytes)
# Read the input file
doc = None
with open(sys.argv[3], 'rb') as fd:
doc = fd.read()
outDoc = signWrapper(algo, private_key, public_key, doc)
with open(sys.argv[4], 'wb') as fd:
fd.write(cbor.dumps(outDoc, sort_keys=True))
if __name__ == '__main__':
main()

View File

@ -1,411 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# ----------------------------------------------------------------------------
# Copyright 2019 ARM Limited or its affiliates
#
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ----------------------------------------------------------------------------
import json
import cbor
import binascii
import uuid
import os
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives import serialization
COSE_ALG = 1
COSE_Sign_Tag = 98
APPLICATION_OCTET_STREAM_ID = 42
ES256 = -7
EDDSA = -8
SUIT_Authentication_Wrapper = 1
SUIT_Manifest = 2
SUIT_Dependency_Resolution = 7
SUIT_Payload_Fetch = 8
SUIT_Install = 9
SUIT_Text = 13
SUIT_Coswid = 14
SUIT_Manifest_Version = 1
SUIT_Manifest_Sequence_Number = 2
SUIT_Dependencies = 3
SUIT_Components = 4
SUIT_Dependency_Components = 5
SUIT_Common = 6
SUIT_Dependency_Resolution = 7
SUIT_Payload_Fetch = 8
SUIT_Install = 9
SUIT_Validate = 10
SUIT_Load = 11
SUIT_Run = 12
SUIT_Text = 13
SUIT_Coswid = 14
SUIT_Dependency_Digest = 1
SUIT_Dependency_Prefix = 2
SUIT_Component_Identifier = 1
SUIT_Component_Size = 2
SUIT_Component_Digest = 3
SUIT_Component_Dependency_Index = 2
SUIT_Condition_Vendor_Identifier = 1
SUIT_Condition_Class_Identifier = 2
SUIT_Condition_Device_Identifier = 3
SUIT_Condition_Image_Match = 4
SUIT_Condition_Image_Not_Match = 5
SUIT_Condition_Use_Before = 6
SUIT_Condition_Minimum_Battery = 7
SUIT_Condition_Update_Authorised = 8
SUIT_Condition_Version = 9
SUIT_Condition_Component_Offset = 10
SUIT_Directive_Set_Component_Index = 11
SUIT_Directive_Set_Manifest_Index = 12
SUIT_Directive_Run_Sequence = 13
SUIT_Directive_Run_Sequence_Conditional = 14
SUIT_Directive_Process_Dependency = 15
SUIT_Directive_Set_Parameters = 16
SUIT_Directive_Override_Parameters = 19
SUIT_Directive_Fetch = 20
SUIT_Directive_Copy = 21
SUIT_Directive_Run = 22
SUIT_Directive_Wait = 23
SUIT_Parameter_Strict_Order = 1
SUIT_Parameter_Coerce_Condition_Failure = 2
SUIT_Parameter_Vendor_ID = 3
SUIT_Parameter_Class_ID = 4
SUIT_Parameter_Device_ID = 5
SUIT_Parameter_URI_List = 6
SUIT_Parameter_Encryption_Info = 7
SUIT_Parameter_Compression_Info = 8
SUIT_Parameter_Unpack_Info = 9
SUIT_Parameter_Source_Component = 10
SUIT_Parameter_Image_Digest = 11
SUIT_Parameter_Image_Size = 12
SUIT_Compression_Algorithm = 1
def obj2bytes(o):
if isinstance(o, int):
l = []
while o:
l.append(o&0xff)
o = o >> 8
return bytes(l)
if isinstance(o, str):
return o.encode('utf-8')
if isinstance(o, bytes):
return o
return b''
def make_SUIT_Components(unused, components):
comps = []
for component in components:
c = {
SUIT_Component_Identifier : [obj2bytes(x) for x in component["id"]]
}
if "digest" in component:
c[SUIT_Component_Digest] = [1, binascii.a2b_hex(component["digest"])]
if "size" in component:
c[SUIT_Component_Size] = component["size"]
comps.append(c)
return (SUIT_Components, comps)
def make_SUIT_Compression_Info(info):
algorithms = {
'gzip' : 1,
'bzip2' : 2,
'deflate' : 3,
'lz4' : 4,
'lzma' : 7,
}
cinfo = {
SUIT_Compression_Algorithm :algorithms[info['algorithm']]
}
def make_SUIT_Set_Parameters(parameters):
set_parameters = {}
SUIT_Parameters_Keys = {
# SUIT_Parameter_Strict_Order = 1
# SUIT_Parameter_Coerce_Condition_Failure = 2
# SUIT_Parameter_Vendor_ID = 3
# SUIT_Parameter_Class_ID = 4
# SUIT_Parameter_Device_ID = 5
# SUIT_Parameter_URI_List = 6
'uris' : lambda x: (SUIT_Parameter_URI_List, cbor.dumps(x)),
# SUIT_Parameter_Encryption_Info = 7
# SUIT_Parameter_Compression_Info = 8
'compression-info': lambda x : (
SUIT_Parameter_Compression_Info,
cbor.dumps(make_SUIT_Compression_Info(x))
),
# SUIT_Parameter_Unpack_Info = 9
'source-index' : lambda x :(SUIT_Parameter_Source_Component, int(x)),
'image-digest' : lambda x :(SUIT_Parameter_Image_Digest, cbor.dumps(x, sort_keys=True)),
'image-size' : lambda x :(SUIT_Parameter_Image_Size, int(x)),
}
for p in parameters:
if p in SUIT_Parameters_Keys:
k, v = SUIT_Parameters_Keys[p](parameters[p])
set_parameters[k] = v
else:
raise Exception('ERROR: {} not found!'.format(p))
return (SUIT_Directive_Set_Parameters, set_parameters)
def make_SUIT_Sequence(seq_name, sequence):
seq = []
SUIT_Sequence_Keys = {
"condition-vendor-id" : lambda x : (SUIT_Condition_Vendor_Identifier, uuid.UUID(x).bytes),
"condition-class-id" : lambda x : (SUIT_Condition_Class_Identifier, uuid.UUID(x).bytes),
"condition-device-id" : lambda x : (SUIT_Condition_Device_Identifier, uuid.UUID(x).bytes),
"condition-image" : lambda x : (SUIT_Condition_Image_Match, None),
"condition-not-image" : lambda x : (SUIT_Condition_Image_Not_Match, None),
# SUIT_Condition_Use_Before = 6
# SUIT_Condition_Minimum_Battery = 7
# SUIT_Condition_Update_Authorised = 8
# SUIT_Condition_Version = 9
"condition-component-offset" : lambda x: (SUIT_Condition_Component_Offset, int(x)),
#
"directive-set-component" : lambda x : (SUIT_Directive_Set_Component_Index, x),
# SUIT_Directive_Set_Manifest_Index = 12
# SUIT_Directive_Run_Sequence = 13
# SUIT_Directive_Run_Sequence_Conditional = 14
"directive-run-conditional" : lambda x : (
SUIT_Directive_Run_Sequence_Conditional,
cbor.dumps(make_SUIT_Sequence("conditional-sequence", x), sort_keys = True)
),
# SUIT_Directive_Process_Dependency = 15
# SUIT_Directive_Set_Parameters = 16
"directive-set-var" : make_SUIT_Set_Parameters,
# SUIT_Directive_Override_Parameters = 19
"directive-fetch" : lambda x : (SUIT_Directive_Fetch, None),
"directive-copy" : lambda x : (SUIT_Directive_Copy, None),
"directive-run" : lambda x : (SUIT_Directive_Run, None),
# SUIT_Directive_Wait = 23
}
for command in sequence:
com_dict = {}
for c in command:
if c in SUIT_Sequence_Keys:
k, v = SUIT_Sequence_Keys[c](command[c])
com_dict[k] = v
else:
raise Exception('ERROR: {} not found!'.format(c))
seq.append(com_dict)
return seq
def make_SUIT_Manifest(info):
# print(info)
SUIT_Manifest_Keys = {
"structure-version" : lambda y, x: (SUIT_Manifest_Version, x),
"sequence-number" : lambda y, x: (SUIT_Manifest_Sequence_Number, x),
# SUIT_Dependencies = 3
"components" : make_SUIT_Components,
# SUIT_Dependency_Components = 5
"common" : lambda y, x: (SUIT_Common, cbor.dumps(make_SUIT_Sequence(y, x), sort_keys=True)),
# SUIT_Dependency_Resolution = 7
# SUIT_Payload_Fetch = 8
"apply-image" : lambda y, x: (SUIT_Install, cbor.dumps(make_SUIT_Sequence(y, x), sort_keys=True)),
"system-verification": lambda y, x: (SUIT_Validate, cbor.dumps(make_SUIT_Sequence(y, x), sort_keys=True)),
"load-image" : lambda y, x: (SUIT_Load, cbor.dumps(make_SUIT_Sequence(y, x), sort_keys=True)),
"run-image" : lambda y, x: (SUIT_Run, cbor.dumps(make_SUIT_Sequence(y, x), sort_keys=True)),
# SUIT_Text = 13
# SUIT_Coswid = 14
}
manifest = {}
for field in info:
if field in SUIT_Manifest_Keys:
k, v = SUIT_Manifest_Keys[field](field, info[field])
manifest[k] = v
else:
raise Exception('ERROR: {} not found!'.format(field))
# print ('suit-manifest: {}'.format(manifest))
return manifest
def make_SUIT_Outer_Wrapper(info):
Outer_Wrapper = {
SUIT_Authentication_Wrapper : None,
SUIT_Manifest : cbor.dumps(make_SUIT_Manifest(info), sort_keys = True)
}
# print('Outer_Wrapper: {}'.format(Outer_Wrapper))
return Outer_Wrapper
def make_SUIT_Components(unused, components):
comps = []
for component in components:
c = {
SUIT_Component_Identifier : [obj2bytes(x) for x in component["id"]]
}
if "digest" in component:
c[SUIT_Component_Digest] = [1, binascii.a2b_hex(component["digest"])]
if "size" in component:
c[SUIT_Component_Size] = component["size"]
comps.append(c)
return (SUIT_Components, comps)
# Expected input format:
# {
# "digest-type" : "str",
# "structure-version" : 1,
# "sequence-number" : 2,
# "components": [
# {
# "component-id":[bytes()],
# "bootable" : bool(),
# "images" : [
# {
# "conditions" : [
# {"current-digest": bytes()},
# {"target-offset" : int()},
# {"target-id": [bytes()]},
# ],
# "digest": bytes(),
# "size" : int(),
# "uri" : str(),
# }
# ]
# "conditions" : [
# {"current-digest": bytes()},
# {"vendor-id" : bytes()},
# {"class-id" : bytes()},
# {"device-id" : bytes()},
# ]
# }
# ],
# "conditions" : [
# {"vendor-id" : bytes()},
# {"class-id" : bytes()},
# {"device-id" : bytes()},
# ]
# }
def digest_str_to_id(s):
return {
'sha-256' : 1,
'sha-256-128' : 2,
'sha-256-120' : 3,
'sha-256-96' : 4,
'sha-256-64' : 5,
'sha-256-32' : 6,
'sha-384' : 7,
'sha-512' : 8,
'sha3-224' : 9,
'sha3-256' : 10,
'sha3-384' : 11,
'sha3-512' : 12,
}.get(s, 1)
def compile_to_suit(suit_info):
digest_id = digest_str_to_id(suit_info.get('digest-type', 'sha-256'))
suit_manifest_desc = {
'structure-version':int(suit_info.get('structure-version', 1)),
'sequence-number':int(suit_info['sequence-number']),
}
#TODO: Dependencies
# Components
components = []
#TODO: dependency components
common = []
dependency_fetch = None
#TODO: Image Fetch when not in streaming mode
fetch_image = None
apply_image = []
# System Verification
#TODO: Dependencies
system_verification = [
{"directive-set-component": True},
{"condition-image": None},
]
#TODO: Load Image
load_image = None
run_image = []
for con in suit_info.get('conditions', []):
common.append(con)
# for each component
for i, comp in enumerate(suit_info['components']):
comp_info = {
'id' : comp['id']
}
components.append(comp_info)
if len(comp['images']) == 1:
set_comp = {"directive-set-component": i}
set_params = {
"directive-set-var" : {
"image-size" : int(comp['images'][0]['size']),
"image-digest" : [digest_id, bytes(comp['images'][0]['digest'])]
}
}
common.append(set_comp)
common.append(set_params)
set_params = {
"directive-set-var" : {
"uris" : [[0, str(comp['images'][0]['uri'])]]
}
}
apply_image.append(set_comp)
apply_image.append(set_params)
else:
for image in comp['images']:
set_comp = {"directive-set-component": i}
set_params = {
"directive-set-var" : {
"image-size" : int(image['size']),
"image-digest" : [digest_id, bytes(image['digest'])]
}
}
conditional_seq = [set_comp] + image.get('conditions',[])[:] + [set_params]
conditional_set_params = {
'directive-run-conditional': conditional_seq
}
common.append(conditional_set_params)
set_params = {
"directive-set-var" : {
"uris" : [[0, str(image['uri'])]]
}
}
conditional_seq = [set_comp] + image.get('conditions',[])[:] + [set_params]
conditional_set_params = {
'directive-run-conditional': conditional_seq
}
apply_image.append(conditional_set_params)
if comp.get('bootable', False):
run_image.append({'directive-set-component' : i})
run_image.append({'directive-run':None})
apply_image.append({"directive-set-component": True})
apply_image.append({"directive-fetch": None})
suit_manifest_desc.update({
"components" : components,
"common" : common,
"apply-image" : apply_image,
"system-verification": system_verification,
"run-image" : run_image,
})
print(suit_manifest_desc)
return cbor.dumps(make_SUIT_Outer_Wrapper(suit_manifest_desc), sort_keys=True)

View File

@ -1,33 +0,0 @@
{
"digest-type" : "sha-256",
"structure-version" : 1,
"sequence-number" : 2,
"components": [
{
"id":["00"],
"bootable" : true,
"images" : [
{
"file" : "encode-04.py",
"uri" : "http://example.com/file.bin",
"conditions" : [
{"condition-component-offset":4096}
]
},
{
"file" : "suit_manifest_encoder_04.py",
"uri" : "http://example.com/file1.bin",
"conditions" : [
{"condition-component-offset":8192}
]
}
]
}
],
"conditions" : [
{"condition-vendor-id" : "fa6b4a53-d5ad-5fdf-be9d-e663e4d41ffe"},
{"condition-class-id" : "1492af14-2569-5e48-bf42-9b2d51f2ab45"}
]
}

View File

@ -1,103 +0,0 @@
#
SUIT_COAP_BASEPATH ?= fw/$(BOARD)
SUIT_COAP_SERVER ?= localhost
SUIT_COAP_ROOT ?= coap://$(SUIT_COAP_SERVER)/$(SUIT_COAP_BASEPATH)
SUIT_COAP_FSROOT ?= $(RIOTBASE)/coaproot
#
SUIT_MANIFEST ?= $(BINDIR_APP)-riot.suitv4.$(APP_VER).bin
SUIT_MANIFEST_LATEST ?= $(BINDIR_APP)-riot.suitv4.latest.bin
SUIT_MANIFEST_SIGNED ?= $(BINDIR_APP)-riot.suitv4_signed.$(APP_VER).bin
SUIT_MANIFEST_SIGNED_LATEST ?= $(BINDIR_APP)-riot.suitv4_signed.latest.bin
SUIT_NOTIFY_VERSION ?= latest
SUIT_NOTIFY_MANIFEST ?= $(APPLICATION)-riot.suitv4_signed.$(SUIT_NOTIFY_VERSION).bin
# Long manifest names require more buffer space when parsing
export CFLAGS += -DCONFIG_SOCK_URLPATH_MAXLEN=128
SUIT_VENDOR ?= "riot-os.org"
SUIT_SEQNR ?= $(APP_VER)
SUIT_CLASS ?= $(BOARD)
#
# SUIT encryption keys
#
# Specify key to use.
# Will use $(SUIT_KEY_DIR)/$(SUIT_KEY) $(SUIT_KEY_DIR)/$(SUIT_KEY).pub as
# private/public key files, similar to how ssh names its key files.
SUIT_KEY ?= default
ifeq (1, $(RIOT_CI_BUILD))
SUIT_KEY_DIR ?= $(BINDIR)
else
SUIT_KEY_DIR ?= $(RIOTBASE)/keys
endif
SUIT_SEC ?= $(SUIT_KEY_DIR)/$(SUIT_KEY)
SUIT_PUB ?= $(SUIT_KEY_DIR)/$(SUIT_KEY).pub
SUIT_PUB_HDR = $(BINDIR)/riotbuild/public_key.h
SUIT_PUB_HDR_DIR = $(dir $(SUIT_PUB_HDR))
CFLAGS += -I$(SUIT_PUB_HDR_DIR)
BUILDDEPS += $(SUIT_PUB_HDR)
$(SUIT_SEC) $(SUIT_PUB): $(CLEAN)
@echo suit: generating key pair in $(SUIT_KEY_DIR)
@mkdir -p $(SUIT_KEY_DIR)
@$(RIOTBASE)/dist/tools/suit_v4/gen_key.py $(SUIT_SEC) $(SUIT_PUB)
# set FORCE so switching between keys using "SUIT_KEY=foo make ..."
# triggers a rebuild even if the new key would otherwise not (because the other
# key's mtime is too far back).
$(SUIT_PUB_HDR): $(SUIT_PUB) FORCE | $(CLEAN)
@mkdir -p $(SUIT_PUB_HDR_DIR)
@cp $(SUIT_PUB) $(SUIT_PUB_HDR_DIR)/public.key
@cd $(SUIT_PUB_HDR_DIR) && xxd -i public.key \
| '$(LAZYSPONGE)' $(LAZYSPONGE_FLAGS) '$@'
suit/genkey: $(SUIT_SEC) $(SUIT_PUB)
#
$(SUIT_MANIFEST): $(SLOT0_RIOT_BIN) $(SLOT1_RIOT_BIN)
$(RIOTBASE)/dist/tools/suit_v4/gen_manifest.py \
--template $(RIOTBASE)/dist/tools/suit_v4/test-2img.json \
--urlroot $(SUIT_COAP_ROOT) \
--seqnr $(SUIT_SEQNR) \
--uuid-vendor $(SUIT_VENDOR) \
--uuid-class $(SUIT_CLASS) \
--offsets $(SLOT0_OFFSET),$(SLOT1_OFFSET) \
-o $@ \
$^
$(SUIT_MANIFEST_SIGNED): $(SUIT_MANIFEST) $(SUIT_SEC) $(SUIT_PUB)
$(RIOTBASE)/dist/tools/suit_v4/sign-04.py \
$(SUIT_SEC) $(SUIT_PUB) $< $@
$(SUIT_MANIFEST_LATEST): $(SUIT_MANIFEST)
@ln -f -s $< $@
$(SUIT_MANIFEST_SIGNED_LATEST): $(SUIT_MANIFEST_SIGNED)
@ln -f -s $< $@
SUIT_MANIFESTS := $(SUIT_MANIFEST) \
$(SUIT_MANIFEST_LATEST) \
$(SUIT_MANIFEST_SIGNED) \
$(SUIT_MANIFEST_SIGNED_LATEST)
suit/manifest: $(SUIT_MANIFESTS)
suit/publish: $(SUIT_MANIFESTS) $(SLOT0_RIOT_BIN) $(SLOT1_RIOT_BIN)
@mkdir -p $(SUIT_COAP_FSROOT)/$(SUIT_COAP_BASEPATH)
@cp $^ $(SUIT_COAP_FSROOT)/$(SUIT_COAP_BASEPATH)
@for file in $^; do \
echo "published \"$$file\""; \
echo " as \"$(SUIT_COAP_ROOT)/$$(basename $$file)\""; \
done
suit/notify: | $(filter suit/publish, $(MAKECMDGOALS))
@test -n "$(SUIT_CLIENT)" || { echo "error: SUIT_CLIENT unset!"; false; }
aiocoap-client -m POST "coap://$(SUIT_CLIENT)/suit/trigger" \
--payload "$(SUIT_COAP_ROOT)/$(SUIT_NOTIFY_MANIFEST)" && \
echo "Triggered $(SUIT_CLIENT) to update."

View File

@ -1,64 +0,0 @@
/*
* Copyright (C) 2019 Koen Zandberg
* 2019 Kaspar Schleiser <kaspar@schleiser.de>
* 2019 Inria
* 2019 Freie Universität Berlin
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup sys_suit_v4
* @brief SUIT v4 manifest handlers
*
* @experimental
*
* @{
*
* @brief Handler functions for SUIT manifests
* @author Koen Zandberg <koen@bergzand.net>
* @author Kaspar Schleiser <kaspar@schleiser.de>
*/
#ifndef SUIT_V4_HANDLERS_H
#define SUIT_V4_HANDLERS_H
#include <stddef.h>
#include <stdint.h>
#include "suit/v4/suit.h"
#include "uuid.h"
#include "nanocbor/nanocbor.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief suit handler prototype
*
* @param manifest SUIT v4 manifest context
* @param it nanocbor_value_t iterator to the content the handler must handle
*
* @return 1 on success
* @return negative on error
*/
typedef int (*suit_manifest_handler_t)(suit_v4_manifest_t *manifest, int key, nanocbor_value_t *it);
/**
* @brief Get suit manifest handler for given integer key
*
* @param[in] key: integer key
*
* @return ptr to handler function
* @return NULL (if handler unavailable or key out of range)
*/
suit_manifest_handler_t suit_manifest_get_manifest_handler(int key);
#ifdef __cplusplus
}
#endif
#endif /* SUIT_V4_HANDLERS_H */
/** @} */

View File

@ -1,58 +0,0 @@
/*
* Copyright (C) 2019 Koen Zandberg
* 2019 Kaspar Schleiser <kaspar@schleiser.de>
* 2019 Inria
* 2019 Freie Universität Berlin
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup sys_suit_v4
* @brief SUIT policy definitions
*
* @{
*
* @brief SUIT policy definitions
* @author Koen Zandberg <koen@bergzand.net>
* @author Kaspar Schleiser <kaspar@schleiser.de>
*
*/
#ifndef SUIT_V4_POLICY_H
#define SUIT_V4_POLICY_H
#include <stddef.h>
#include <stdint.h>
#include "uuid.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @name bitfield of required policies
* @{
*/
#define SUIT_VALIDATED_AUTH 0x1 /**< currently unused */
#define SUIT_VALIDATED_VERSION 0x2 /**< SUIT format version */
#define SUIT_VALIDATED_SEQ_NR 0x4 /**< new seq nr > old seq nr */
#define SUIT_VALIDATED_VENDOR 0x8 /**< vendor UUID matches */
#define SUIT_VALIDATED_CLASS 0x10 /**< class UUID matches */
#define SUIT_VALIDATED_DEVICE 0x20 /**< device UUID matches */
/** @} */
/**
* @brief SUIT default policy
*/
#define SUIT_DEFAULT_POLICY \
(SUIT_VALIDATED_VERSION | SUIT_VALIDATED_SEQ_NR | SUIT_VALIDATED_VENDOR | SUIT_VALIDATED_CLASS)
#ifdef __cplusplus
}
#endif
#endif /* SUIT_V4_POLICY_H */
/** @} */

View File

@ -1,224 +0,0 @@
/*
* Copyright (C) 2019 Koen Zandberg
* 2019 Kaspar Schleiser <kaspar@schleiser.de>
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @defgroup sys_suit_v4 SUIT draft v4
* @ingroup sys_suit
* @brief SUIT manifest handling
*
* @{
*
* @brief Handler functions for SUIT manifests
* @author Koen Zandberg <koen@bergzand.net>
* @author Kaspar Schleiser <kaspar@schleiser.de>
*
*/
#ifndef SUIT_V4_SUIT_H
#define SUIT_V4_SUIT_H
#include <stddef.h>
#include <stdint.h>
#include "cose/sign.h"
#include "nanocbor/nanocbor.h"
#include "uuid.h"
#include "riotboot/flashwrite.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Buffer size used for Cose
*/
#ifndef SUIT_COSE_BUF_SIZE
#define SUIT_COSE_BUF_SIZE (512U)
#endif
/**
* @brief Maximum number of components used for SUIT v4
*/
#define SUIT_V4_COMPONENT_MAX (1U)
/**
* @brief Supported SUIT manifest version
*/
#define SUIT_MANIFEST_VERSION (4)
/**
* @brief Current SUIT serialization format version
*
* see https://tools.ietf.org/html/draft-moran-suit-manifest-04#section-8.2 for
* details
*/
#define SUIT_VERSION (1)
/**
* @brief SUIT error codes
*/
typedef enum {
SUIT_OK = 0, /**< Manifest parsed and validated */
SUIT_ERR_INVALID_MANIFEST = -1, /**< Unexpected CBOR structure detected */
SUIT_ERR_UNSUPPORTED = -2, /**< Unsupported SUIT feature detected */
SUIT_ERR_NOT_SUPPORTED = -3, /**< Unsupported manifest features detected */
SUIT_ERR_COND = -4, /**< Conditionals evaluate to false */
SUIT_ERR_SEQUENCE_NUMBER = -5, /**< Sequence number less or equal to
current sequence number */
SUIT_ERR_SIGNATURE = -6, /**< Unable to verify signature */
} suit_v4_error_t;
/**
* @brief SUIT payload digest algorithms
*
* Unofficial list from
* [suit-manifest-generator](https://github.com/ARMmbed/suit-manifest-generator)
*/
typedef enum {
SUIT_DIGEST_NONE = 0, /**< No digest algo supplied */
SUIT_DIGEST_SHA256 = 1, /**< SHA256 */
SUIT_DIGEST_SHA384 = 2, /**< SHA384 */
SUIT_DIGEST_SHA512 = 3, /**< SHA512 */
} suit_v4_digest_t;
/**
* @brief SUIT payload digest types
*
* Unofficial list from
* [suit-manifest-generator](https://github.com/ARMmbed/suit-manifest-generator)
*/
typedef enum {
SUIT_DIGEST_TYPE_RAW = 1, /**< Raw payload digest */
SUIT_DIGEST_TYPE_INSTALLED = 2, /**< Installed firmware digest */
SUIT_DIGEST_TYPE_CIPHERTEXT = 3, /**< Ciphertext digest */
SUIT_DIGEST_TYPE_PREIMAGE = 4 /**< Pre-image digest */
} suit_v4_digest_type_t;
/**
* @brief SUIT component types
*
* Unofficial list from
* [suit-manifest-generator](https://github.com/ARMmbed/suit-manifest-generator)
*/
enum {
SUIT_COMPONENT_IDENTIFIER = 1, /**< Identifier component */
SUIT_COMPONENT_SIZE = 2, /**< Size component */
SUIT_COMPONENT_DIGEST = 3, /**< Digest component */
};
/**
* @brief SUIT v4 component struct
*/
typedef struct {
uint32_t size; /**< Size */
nanocbor_value_t identifier; /**< Identifier*/
nanocbor_value_t url; /**< Url */
nanocbor_value_t digest; /**< Digest */
} suit_v4_component_t;
/**
* @brief SUIT manifest struct
*/
typedef struct {
cose_sign_dec_t verify; /**< COSE signature validation struct */
const uint8_t *buf; /**< ptr to the buffer of the manifest */
size_t len; /**< length of the manifest */
uint32_t validated; /**< bitfield of validated policies */
uint32_t state; /**< bitfield holding state information */
/** List of components in the manifest */
suit_v4_component_t components[SUIT_V4_COMPONENT_MAX];
unsigned components_len; /**< Current number of components */
int32_t component_current; /**< Current component index */
riotboot_flashwrite_t *writer; /**< Pointer to the riotboot flash writer */
/** Manifest validation buffer */
uint8_t validation_buf[SUIT_COSE_BUF_SIZE];
cose_key_t *key; /**< Ptr to the public key for validation */
char *urlbuf; /**< Buffer containing the manifest url */
size_t urlbuf_len; /**< Length of the manifest url */
} suit_v4_manifest_t;
/**
* @brief Bit flags used to determine if SUIT manifest contains components
*/
#define SUIT_MANIFEST_HAVE_COMPONENTS (0x1)
/**
* @brief Bit flags used to determine if SUIT manifest contains an image
*/
#define SUIT_MANIFEST_HAVE_IMAGE (0x2)
/**
* @brief Parse a manifest
*
* @note The buffer is still required after parsing, please don't reuse the
* buffer while the @p manifest is used
*
* @param[in] manifest manifest context to store information in
* @param[in] buf buffer to parse the manifest from
* @param[in] len length of the manifest data in the buffer
*
* @return SUIT_OK on parseable manifest
* @return negative @ref suit_v4_error_t code on error
*/
int suit_v4_parse(suit_v4_manifest_t *manifest, const uint8_t *buf, size_t len);
/**
* @brief Check a manifest policy
*
* @param[in] manifest manifest context to check the policy for
*
* @return 0 on valid manifest policy
* @return -1 on invalid manifest policy
*/
int suit_v4_policy_check(suit_v4_manifest_t *manifest);
/**
* @brief Iterate over a cbor map container
*
* @param[in] it cbor container iterator
* @param[out] key the returned key
* @param[out] value the returned value
*
* @return 0 when the iterator is already at the end of the container
* @return the number of returned (key, value) pair, e.g. 1
*/
int suit_cbor_map_iterate(nanocbor_value_t *it, nanocbor_value_t *key, nanocbor_value_t *value);
/**
* @brief Parser a cbor subsequence
*
* @param[in] bseq subsequence value
* @param[out] it cbor iterator
*
* @return 0 on success
* @return -1 if bseq is not a cbor string
* @return CborError code on other cbor parser errors
*/
int suit_cbor_subparse(nanocbor_value_t *bseq, nanocbor_value_t *it);
/**
* @brief Helper function for writing bytes on flash a specified offset
*
* @param[in] arg ptr to the SUIT manifest
* @param[in] offset offset to write to on flash
* @param[in] buf bytes to write
* @param[in] len length of bytes to write
* @param[in] more whether more data is coming
*
* @return 0 on success
* @return <0 on error
*/
int suit_flashwrite_helper(void *arg, size_t offset, uint8_t *buf, size_t len,
int more);
#ifdef __cplusplus
}
#endif
#endif /* SUIT_V4_SUIT_H */
/** @} */

View File

@ -1,2 +0,0 @@
MODULE := suit_v4
include $(RIOTBASE)/Makefile.base

View File

@ -1,188 +0,0 @@
/*
* Copyright (C) 2018 Freie Universität Berlin
* Copyright (C) 2018 Inria
* 2019 Kaspar Schleiser <kaspar@schleiser.de>
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup sys_suit
* @{
*
* @file
* @brief SUIT manifest parser library for CBOR based manifests
*
* @author Koen Zandberg <koen@bergzand.net>
* @author Kaspar Schleiser <kaspar@schleiser.de>
*
* @}
*/
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include "suit/v4/handlers.h"
#include "suit/v4/suit.h"
#include "suit/v4/policy.h"
#include "nanocbor/nanocbor.h"
#include "cose/sign.h"
#include "public_key.h"
#include "log.h"
#define ENABLE_DEBUG (0)
#include "debug.h"
static suit_manifest_handler_t _manifest_get_auth_wrapper_handler(int key);
typedef suit_manifest_handler_t (*suit_manifest_handler_getter_t)(int key);
int suit_cbor_subparse(nanocbor_value_t *bseq, nanocbor_value_t *it)
{
const uint8_t *bytes;
size_t bytes_len = 0;
int res = nanocbor_get_bstr(bseq, &bytes, &bytes_len);
if (res < 0) {
return SUIT_ERR_INVALID_MANIFEST;
}
nanocbor_decoder_init(it, bytes, bytes_len);
return SUIT_OK;
}
int _v4_parse(suit_v4_manifest_t *manifest, const uint8_t *buf,
size_t len, suit_manifest_handler_getter_t getter)
{
nanocbor_value_t it, map;
nanocbor_decoder_init(&it, buf, len);
map = it;
if (nanocbor_enter_map(&map, &it) < 0) {
LOG_DEBUG("suit _v4_parse(): manifest not a map!\n");
return SUIT_ERR_INVALID_MANIFEST;
}
while (!nanocbor_at_end(&it)) {
int32_t integer_key;
if (nanocbor_get_int32(&it, &integer_key) < 0) {
return SUIT_ERR_INVALID_MANIFEST;
}
LOG_DEBUG("got key val=%" PRIi32 "\n", integer_key);
suit_manifest_handler_t handler = getter(integer_key);
nanocbor_value_t value = it;
nanocbor_skip(&it);
if (handler) {
int res = handler(manifest, integer_key, &value);
if (res < 0) {
LOG_INFO("handler returned <0\n)");
return SUIT_ERR_INVALID_MANIFEST;
}
}
else {
LOG_DEBUG("no handler found\n");
}
}
nanocbor_leave_container(&map, &it);
return SUIT_OK;
}
int suit_v4_parse(suit_v4_manifest_t *manifest, const uint8_t *buf,
size_t len)
{
manifest->buf = buf;
manifest->len = len;
return _v4_parse(manifest, buf, len, _manifest_get_auth_wrapper_handler);
}
static int _auth_handler(suit_v4_manifest_t *manifest, int key,
nanocbor_value_t *it)
{
(void)key;
const uint8_t *cose_buf;
size_t cose_len = 0;
int res = nanocbor_get_bstr(it, &cose_buf, &cose_len);
if (res < 0) {
LOG_INFO("Unable to get COSE signature\n");
return SUIT_ERR_INVALID_MANIFEST;
}
res = cose_sign_decode(&manifest->verify, cose_buf, cose_len);
if (res < 0) {
LOG_INFO("Unable to parse COSE signature\n");
return SUIT_ERR_INVALID_MANIFEST;
}
return 0;
}
static int _manifest_handler(suit_v4_manifest_t *manifest, int key,
nanocbor_value_t *it)
{
(void)key;
const uint8_t *manifest_buf;
size_t manifest_len;
nanocbor_get_bstr(it, &manifest_buf, &manifest_len);
/* Validate the COSE struct first now that we have the payload */
cose_sign_decode_set_payload(&manifest->verify, manifest_buf, manifest_len);
/* Iterate over signatures, should only be a single signature */
cose_signature_dec_t signature;
cose_sign_signature_iter_init(&signature);
if (!cose_sign_signature_iter(&manifest->verify, &signature)) {
return SUIT_ERR_INVALID_MANIFEST;
}
/* Initialize key from hardcoded public key */
cose_key_t pkey;
cose_key_init(&pkey);
cose_key_set_keys(&pkey, COSE_EC_CURVE_ED25519, COSE_ALGO_EDDSA,
public_key, NULL, NULL);
LOG_INFO("suit: verifying manifest signature...\n");
int verification = cose_sign_verify(&manifest->verify, &signature,
&pkey, manifest->validation_buf,
SUIT_COSE_BUF_SIZE);
if (verification != 0) {
LOG_INFO("Unable to validate signature\n");
return SUIT_ERR_SIGNATURE;
}
return _v4_parse(manifest, manifest_buf,
manifest_len, suit_manifest_get_manifest_handler);
}
static suit_manifest_handler_t _suit_manifest_get_handler(int key,
const suit_manifest_handler_t *handlers,
size_t len)
{
if (key < 0 || (size_t)key >= len) {
return NULL;
}
return handlers[key];
}
/* begin{code-style-ignore} */
static const suit_manifest_handler_t _auth_handlers[] = {
[ 0] = NULL,
[ 1] = _auth_handler,
[ 2] = _manifest_handler,
};
/* end{code-style-ignore} */
static const unsigned _auth_handlers_len = ARRAY_SIZE(_auth_handlers);
static suit_manifest_handler_t _manifest_get_auth_wrapper_handler(int key)
{
return _suit_manifest_get_handler(key, _auth_handlers,
_auth_handlers_len);
}

View File

@ -1,516 +0,0 @@
/*
* Copyright (C) 2019 Koen Zandberg
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup sys_suit_v4
* @{
*
* @file
* @brief SUIT v4
*
* @author Koen Zandberg <koen@bergzand.net>
*
* @}
*/
#include <inttypes.h>
#include "suit/coap.h"
#include "suit/conditions.h"
#include "suit/v4/suit.h"
#include "suit/v4/handlers.h"
#include "suit/v4/policy.h"
#include "suit/v4/suit.h"
#include "riotboot/hdr.h"
#include "riotboot/slot.h"
#include <nanocbor/nanocbor.h>
#include "log.h"
#define HELLO_HANDLER_MAX_STRLEN 32
static int _handle_command_sequence(suit_v4_manifest_t *manifest, nanocbor_value_t *it,
suit_manifest_handler_t handler);
static int _common_handler(suit_v4_manifest_t *manifest, int key, nanocbor_value_t *it);
static int _common_sequence_handler(suit_v4_manifest_t *manifest, int key, nanocbor_value_t *it);
static int _validate_uuid(suit_v4_manifest_t *manifest, nanocbor_value_t *it, uuid_t *uuid)
{
(void)manifest;
const uint8_t *uuid_manifest_ptr;
size_t len = sizeof(uuid_t);
char uuid_str[UUID_STR_LEN + 1];
char uuid_str2[UUID_STR_LEN + 1];
if (nanocbor_get_bstr(it, &uuid_manifest_ptr, &len) < 0) {
return SUIT_ERR_INVALID_MANIFEST;
}
uuid_to_string((uuid_t *)uuid_manifest_ptr, uuid_str);
uuid_to_string(uuid, uuid_str2);
LOG_INFO("Comparing %s to %s from manifest\n", uuid_str2, uuid_str);
return uuid_equal(uuid, (uuid_t *)uuid_manifest_ptr) ? 0 : -1;
}
static int _cond_vendor_handler(suit_v4_manifest_t *manifest, int key, nanocbor_value_t *it)
{
(void)key;
LOG_INFO("validating vendor ID\n");
int rc = _validate_uuid(manifest, it, suit_get_vendor_id());
if (rc == SUIT_OK) {
LOG_INFO("validating vendor ID: OK\n");
manifest->validated |= SUIT_VALIDATED_VENDOR;
}
return rc;
}
static int _cond_class_handler(suit_v4_manifest_t *manifest, int key, nanocbor_value_t *it)
{
(void)key;
LOG_INFO("validating class id\n");
int rc = _validate_uuid(manifest, it, suit_get_class_id());
if (rc == SUIT_OK) {
LOG_INFO("validating class id: OK\n");
manifest->validated |= SUIT_VALIDATED_CLASS;
}
return rc;
}
static int _cond_comp_offset(suit_v4_manifest_t *manifest, int key, nanocbor_value_t *it)
{
(void)manifest;
(void)key;
uint32_t offset;
nanocbor_get_uint32(it, &offset);
uint32_t other_offset = (uint32_t)riotboot_slot_offset(riotboot_slot_other());
LOG_INFO("Comparing manifest offset %u with other slot offset %u\n",
(unsigned)offset, (unsigned)other_offset);
return other_offset == offset ? 0 : -1;
}
static int _dtv_set_comp_idx(suit_v4_manifest_t *manifest, int key, nanocbor_value_t *it)
{
(void)key;
if (nanocbor_get_type(it) == NANOCBOR_TYPE_FLOAT) {
LOG_DEBUG("_dtv_set_comp_idx() ignoring boolean and floats\n)");
nanocbor_skip(it);
}
else if (nanocbor_get_int32(it, &manifest->component_current) < 0) {
return SUIT_ERR_INVALID_MANIFEST;
}
LOG_DEBUG("Setting component index to %d\n", (int)manifest->component_current);
return 0;
}
static int _dtv_run_seq_cond(suit_v4_manifest_t *manifest, int key, nanocbor_value_t *it)
{
(void)key;
LOG_DEBUG("Starting conditional sequence handler\n");
_handle_command_sequence(manifest, it, _common_sequence_handler);
return 0;
}
static int _param_get_uri_list(suit_v4_manifest_t *manifest, nanocbor_value_t *it)
{
LOG_DEBUG("got url list\n");
manifest->components[manifest->component_current].url = *it;
return 0;
}
static int _param_get_digest(suit_v4_manifest_t *manifest, nanocbor_value_t *it)
{
LOG_DEBUG("got digest\n");
manifest->components[manifest->component_current].digest = *it;
return 0;
}
static int _param_get_img_size(suit_v4_manifest_t *manifest, nanocbor_value_t *it)
{
int res = nanocbor_get_uint32(it, &manifest->components[0].size);
if (res < 0) {
LOG_DEBUG("error getting image size\n");
return res;
}
return res;
}
static int _dtv_set_param(suit_v4_manifest_t *manifest, int key, nanocbor_value_t *it)
{
(void)key;
/* `it` points to the entry of the map containing the type and value */
nanocbor_value_t map;
nanocbor_enter_map(it, &map);
while (!nanocbor_at_end(&map)) {
/* map points to the key of the param */
int32_t param_key;
nanocbor_get_int32(&map, &param_key);
LOG_DEBUG("Setting component index to %" PRIi32 "\n", manifest->component_current);
LOG_DEBUG("param_key=%" PRIi32 "\n", param_key);
int res;
switch (param_key) {
case 6: /* SUIT URI LIST */
res = _param_get_uri_list(manifest, &map);
break;
case 11: /* SUIT DIGEST */
res = _param_get_digest(manifest, &map);
break;
case 12: /* SUIT IMAGE SIZE */
res = _param_get_img_size(manifest, &map);
break;
default:
res = -1;
}
nanocbor_skip(&map);
if (res) {
return res;
}
}
return SUIT_OK;
}
static int _dtv_fetch(suit_v4_manifest_t *manifest, int key, nanocbor_value_t *_it)
{
(void)key; (void)_it; (void)manifest;
LOG_DEBUG("_dtv_fetch() key=%i\n", key);
const uint8_t *url;
size_t url_len;
/* TODO: there must be a simpler way */
{
/* the url list is a binary sequence containing a cbor array of
* (priority, url) tuples (represented as array with length two)
* */
nanocbor_value_t it;
/* open sequence with cbor parser */
int err = suit_cbor_subparse(&manifest->components[0].url, &it);
if (err < 0) {
LOG_DEBUG("subparse failed\n)");
return err;
}
nanocbor_value_t url_it;
/* enter container, confirm it is an array, too */
if (nanocbor_enter_array(&it, &url_it) < 0) {
LOG_DEBUG("url list no array\n)");
return SUIT_ERR_INVALID_MANIFEST;
}
nanocbor_value_t url_value_it;
if (nanocbor_enter_array(&url_it, &url_value_it) < 0) {
LOG_DEBUG("url entry no array\n)");
return SUIT_ERR_INVALID_MANIFEST;
}
/* expect two entries: priority as int, url as byte string. bail out if not. */
uint32_t prio;
/* check that first array entry is an int (the priority of the url) */
if (nanocbor_get_uint32(&url_value_it, &prio) < 0) {
LOG_DEBUG("expected URL priority (int), got %d\n",
nanocbor_get_type(&url_value_it));
return -1;
}
LOG_DEBUG("URL priority %"PRIu32"\n", prio);
int res = nanocbor_get_tstr(&url_value_it, &url, &url_len);
if (res < 0) {
LOG_DEBUG("error parsing URL\n)");
return SUIT_ERR_INVALID_MANIFEST;
}
if (url_len >= manifest->urlbuf_len) {
LOG_INFO("url too large: %u>%u\n)", (unsigned)url_len,
(unsigned)manifest->urlbuf_len);
return SUIT_ERR_UNSUPPORTED;
}
memcpy(manifest->urlbuf, url, url_len);
manifest->urlbuf[url_len] = '\0';
nanocbor_leave_container(&url_it, &url_value_it);
nanocbor_leave_container(&it, &url_it);
}
LOG_DEBUG("_dtv_fetch() fetching \"%s\" (url_len=%u)\n", manifest->urlbuf,
(unsigned)url_len);
int target_slot = riotboot_slot_other();
riotboot_flashwrite_init(manifest->writer, target_slot);
int res = suit_coap_get_blockwise_url(manifest->urlbuf, COAP_BLOCKSIZE_64,
suit_flashwrite_helper, manifest);
if (res) {
LOG_INFO("image download failed\n)");
return res;
}
const uint8_t *digest;
size_t digest_len;
res = nanocbor_get_bstr(&manifest->components[0].digest, &digest, &digest_len);
if (res < 0) {
return SUIT_ERR_INVALID_MANIFEST;
}
/* "digest" points to a 36 byte string that includes the digest type.
* riotboot_flashwrite_verify_sha256() is only interested in the 32b digest,
* so shift the pointer accordingly.
*/
res = riotboot_flashwrite_verify_sha256(digest + 4, manifest->components[0].size,
target_slot);
if (res) {
LOG_INFO("image verification failed\n");
return res;
}
manifest->state |= SUIT_MANIFEST_HAVE_IMAGE;
return SUIT_OK;
}
static int _version_handler(suit_v4_manifest_t *manifest, int key,
nanocbor_value_t *it)
{
(void)manifest;
(void)key;
/* Validate manifest version */
int32_t version = -1;
if (nanocbor_get_int32(it, &version) >= 0) {
if (version == SUIT_VERSION) {
manifest->validated |= SUIT_VALIDATED_VERSION;
LOG_INFO("suit: validated manifest version\n)");
return 0;
}
else {
return -1;
}
}
return -1;
}
static int _seq_no_handler(suit_v4_manifest_t *manifest, int key, nanocbor_value_t *it)
{
(void)key;
int32_t seq_nr;
if (nanocbor_get_int32(it, &seq_nr) < 0) {
LOG_INFO("Unable to get sequence number\n");
return SUIT_ERR_INVALID_MANIFEST;
}
const riotboot_hdr_t *hdr = riotboot_slot_get_hdr(riotboot_slot_current());
if (seq_nr <= (int32_t)hdr->version) {
LOG_INFO("%"PRId32" <= %"PRId32"\n", seq_nr, hdr->version);
LOG_INFO("seq_nr <= running image\n)");
return -1;
}
hdr = riotboot_slot_get_hdr(riotboot_slot_other());
if (riotboot_hdr_validate(hdr) == 0) {
if (seq_nr<= (int32_t)hdr->version) {
LOG_INFO("%"PRIu32" <= %"PRIu32"\n", seq_nr, hdr->version);
LOG_INFO("seq_nr <= other image\n)");
return -1;
}
}
LOG_INFO("suit: validated sequence number\n)");
manifest->validated |= SUIT_VALIDATED_SEQ_NR;
return 0;
}
static int _dependencies_handler(suit_v4_manifest_t *manifest, int key,
nanocbor_value_t *it)
{
(void)manifest;
(void)key;
(void)it;
/* No dependency support */
return 0;
}
static int _component_handler(suit_v4_manifest_t *manifest, int key,
nanocbor_value_t *it)
{
(void)manifest;
(void)key;
nanocbor_value_t arr;
LOG_DEBUG("storing components\n)");
if (nanocbor_enter_array(it, &arr) < 0) {
LOG_DEBUG("components field not an array\n");
return -1;
}
unsigned n = 0;
while (!nanocbor_at_end(&arr)) {
nanocbor_value_t map;
if (n < SUIT_V4_COMPONENT_MAX) {
manifest->components_len += 1;
}
else {
LOG_DEBUG("too many components\n)");
return SUIT_ERR_INVALID_MANIFEST;
}
if (nanocbor_enter_map(&arr, &map) < 0) {
LOG_DEBUG("suit _v4_parse(): manifest not a map!\n");
return SUIT_ERR_INVALID_MANIFEST;
}
suit_v4_component_t *current = &manifest->components[n];
while (!nanocbor_at_end(&map)) {
/* handle key, value */
int32_t integer_key;
if (nanocbor_get_int32(&map, &integer_key) < 0) {
return SUIT_ERR_INVALID_MANIFEST;
}
switch (integer_key) {
case SUIT_COMPONENT_IDENTIFIER:
current->identifier = map;
break;
case SUIT_COMPONENT_SIZE:
LOG_DEBUG("skipping SUIT_COMPONENT_SIZE");
break;
case SUIT_COMPONENT_DIGEST:
current->digest = map;
break;
default:
LOG_DEBUG("ignoring unexpected component data (nr. %" PRIi32 ")\n",
integer_key);
}
nanocbor_skip(&map);
LOG_DEBUG("component %u parsed\n", n);
}
nanocbor_leave_container(&arr, &map);
n++;
}
manifest->state |= SUIT_MANIFEST_HAVE_COMPONENTS;
nanocbor_leave_container(it, &arr);
LOG_DEBUG("storing components done\n)");
return 0;
}
/* begin{code-style-ignore} */
static const suit_manifest_handler_t global_handlers[] = {
[ 0] = NULL,
[ 1] = _version_handler,
[ 2] = _seq_no_handler,
[ 3] = _dependencies_handler,
[ 4] = _component_handler,
[ 5] = NULL,
[ 6] = _common_handler,
[ 9] = _common_handler,
};
/* end{code-style-ignore} */
static const unsigned global_handlers_len = ARRAY_SIZE(global_handlers);
/* begin{code-style-ignore} */
static const suit_manifest_handler_t _sequence_handlers[] = {
[ 0] = NULL,
[ 1] = _cond_vendor_handler,
[ 2] = _cond_class_handler,
[10] = _cond_comp_offset,
/* Directives */
[11] = _dtv_set_comp_idx,
/* [12] = _dtv_set_man_idx, */
/* [13] = _dtv_run_seq, */
[14] = _dtv_run_seq_cond,
[16] = _dtv_set_param,
[20] = _dtv_fetch,
/* [22] = _dtv_run, */
};
/* end{code-style-ignore} */
static const unsigned _sequence_handlers_len = ARRAY_SIZE(_sequence_handlers);
static suit_manifest_handler_t _suit_manifest_get_handler(int key,
const suit_manifest_handler_t *handlers,
size_t len)
{
if (key < 0 || (size_t)key >= len) {
return NULL;
}
return handlers[key];
}
suit_manifest_handler_t suit_manifest_get_manifest_handler(int key)
{
return _suit_manifest_get_handler(key, global_handlers,
global_handlers_len);
}
static int _common_sequence_handler(suit_v4_manifest_t *manifest, int key,
nanocbor_value_t *it)
{
suit_manifest_handler_t handler = _suit_manifest_get_handler(key,
_sequence_handlers,
_sequence_handlers_len);
LOG_DEBUG("Handling handler with key %d at %p\n", key, handler);
if (handler) {
return handler(manifest, key, it);
}
else {
LOG_DEBUG("Sequence handler not implemented, ID: %d\n", key);
return -1;
}
}
static int _common_handler(suit_v4_manifest_t *manifest, int key, nanocbor_value_t *it)
{
(void)key;
return _handle_command_sequence(manifest, it, _common_sequence_handler);
}
int _handle_command_sequence(suit_v4_manifest_t *manifest, nanocbor_value_t *bseq,
suit_manifest_handler_t handler)
{
LOG_DEBUG("Handling command sequence\n");
nanocbor_value_t it, arr;
int err = suit_cbor_subparse(bseq, &it);
if (err < 0) {
return err;
}
if (nanocbor_enter_array(&it, &arr) < 0) {
return SUIT_ERR_INVALID_MANIFEST;
}
while (!nanocbor_at_end(&arr)) {
nanocbor_value_t map;
if (nanocbor_enter_map(&arr, &map) < 0) {
return SUIT_ERR_INVALID_MANIFEST;
}
int32_t integer_key;
if (nanocbor_get_int32(&map, &integer_key) < 0) {
return SUIT_ERR_INVALID_MANIFEST;
}
int res = handler(manifest, integer_key, &map);
if (res < 0) {
LOG_DEBUG("Sequence handler error\n");
return res;
}
nanocbor_leave_container(&arr, &map);
}
nanocbor_leave_container(&it, &arr);
return 0;
}

View File

@ -1,37 +0,0 @@
/*
* Copyright (C) 2019 Kaspar Schleiser <kaspar@schleiser.de>
* 2019 Inria
* 2019 Freie Universität Berlin
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup sys_suit_v4
* @{
*
* @file
* @brief SUIT v4 policy checking code
*
* @author Kaspar Schleiser <kaspar@schleiser.de>
*
* @}
*/
#include "suit/v4/suit.h"
#include "suit/v4/policy.h"
#include "log.h"
int suit_v4_policy_check(suit_v4_manifest_t *manifest)
{
if (SUIT_DEFAULT_POLICY & ~(manifest->validated)) {
LOG_INFO("SUIT policy check failed!\n");
return -1;
}
else {
LOG_INFO("SUIT policy check OK.\n");
return 0;
}
}