Skip to content

Add secure boot support for compile command. #1686

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

Merged
merged 20 commits into from
Mar 24, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
04b8d5c
add flags to allow the override of the keys used to sign and encrypt …
umbynos Mar 4, 2022
3a8972f
add integration test for ReplaceSecurityKeys() function
umbynos Mar 7, 2022
35294ce
fix regression introduced: target platform could be nil so using befo…
umbynos Mar 8, 2022
50cc358
apply suggestions from code review
umbynos Mar 9, 2022
04a41e7
rename of some flags (done to accommodate the proposed changes in pla…
umbynos Mar 10, 2022
e96fc18
change approach: override keys using `builderCtx.CustomBuildProperties`
umbynos Mar 10, 2022
900c0e1
add check in the builder regarding the usage of "build.keys.type" pro…
umbynos Mar 11, 2022
87a19ea
add secure boot to the platform specifications
umbynos Mar 14, 2022
dd05e7d
Apply suggestions from code review
umbynos Mar 15, 2022
583dd9c
modify the check on in the builder regarding the usage of "build.keys…
umbynos Mar 16, 2022
33e4c0d
remove check on the flags specifying the keys, it's the tool responsi…
umbynos Mar 16, 2022
6262cfa
move content to a guides section
umbynos Mar 16, 2022
2ed7002
add specifications regarding `build.keys` properties
umbynos Mar 16, 2022
06f4beb
Apply suggestions from code review
umbynos Mar 17, 2022
c18bbca
add link to external resource to provide a quick explanation of the r…
umbynos Mar 17, 2022
3510509
change `tools.imgtool.build.pattern` to `tools.imgtool.flags`
umbynos Mar 17, 2022
1d2e03e
add small section explaining why is recommended to use these prop names
umbynos Mar 17, 2022
47c3917
Apply suggestions from code review
umbynos Mar 18, 2022
d480ae5
Correct error message
umbynos Mar 23, 2022
c40e6cb
Apply suggestions from code review
umbynos Mar 24, 2022
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
12 changes: 12 additions & 0 deletions cli/arguments/arguments.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,15 @@ func CheckFlagsConflicts(command *cobra.Command, flagNames ...string) {
feedback.Errorf(tr("Can't use %s flags at the same time.", "--"+strings.Join(flagNames, " "+tr("and")+" --")))
os.Exit(errorcodes.ErrBadArgument)
}

// CheckFlagsMandatory is a helper function useful to report errors when at least one flag is not used in a group of "required" flags
func CheckFlagsMandatory(command *cobra.Command, flagNames ...string) {
for _, flagName := range flagNames {
if command.Flag(flagName).Changed {
continue
} else {
feedback.Errorf(tr("Flag %[1]s is mandatory when used in conjunction with flag %[2]s.", "--"+flagName, "--"+strings.Join(flagNames, " "+tr("and")+" --")))
os.Exit(errorcodes.ErrBadArgument)
}
}
}
16 changes: 16 additions & 0 deletions cli/compile/compile.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ var (
buildCachePath string // Builds of 'core.a' are saved into this path to be cached and reused.
buildPath string // Path where to save compiled files.
buildProperties []string // List of custom build properties separated by commas. Or can be used multiple times for multiple properties.
keysKeychain string // The path of the dir where to search for the custom keys to sign and encrypt a binary. Used only by the platforms that supports it
signKey string // The name of the custom signing key to use to sign a binary during the compile process. Used only by the platforms that supports it
encryptKey string // The name of the custom encryption key to use to encrypt a binary during the compile process. Used only by the platforms that supports it
warnings string // Used to tell gcc which warning level to use.
verbose bool // Turns on verbose mode.
quiet bool // Suppresses almost every output.
Expand Down Expand Up @@ -100,6 +103,12 @@ func NewCommand() *cobra.Command {
tr("List of custom build properties separated by commas. Or can be used multiple times for multiple properties."))
compileCommand.Flags().StringArrayVar(&buildProperties, "build-property", []string{},
tr("Override a build property with a custom value. Can be used multiple times for multiple properties."))
compileCommand.Flags().StringVar(&keysKeychain, "keys-keychain", "",
tr("The path of the dir to search for the custom keys to sign and encrypt a binary. Used only by the platforms that support it."))
compileCommand.Flags().StringVar(&signKey, "sign-key", "",
tr("The name of the custom signing key to use to sign a binary during the compile process. Used only by the platforms that support it."))
compileCommand.Flags().StringVar(&encryptKey, "encrypt-key", "",
tr("The name of the custom encryption key to use to encrypt a binary during the compile process. Used only by the platforms that support it."))
compileCommand.Flags().StringVar(&warnings, "warnings", "none",
tr(`Optional, can be: %s. Used to tell gcc which warning level to use (-W flag).`, "none, default, more, all"))
compileCommand.Flags().BoolVarP(&verbose, "verbose", "v", false, tr("Optional, turns on verbose mode."))
Expand Down Expand Up @@ -142,6 +151,10 @@ func runCompileCommand(cmd *cobra.Command, args []string) {

sketchPath := arguments.InitSketchPath(path)

if keysKeychain != "" || signKey != "" || encryptKey != "" {
arguments.CheckFlagsMandatory(cmd, "keys-keychain", "sign-key", "encrypt-key")
}

var overrides map[string]string
if sourceOverrides != "" {
data, err := paths.New(sourceOverrides).ReadFile()
Expand Down Expand Up @@ -198,6 +211,9 @@ func runCompileCommand(cmd *cobra.Command, args []string) {
CreateCompilationDatabaseOnly: compilationDatabaseOnly,
SourceOverride: overrides,
Library: library,
KeysKeychain: keysKeychain,
SignKey: signKey,
EncryptKey: encryptKey,
}
compileStdOut := new(bytes.Buffer)
compileStdErr := new(bytes.Buffer)
Expand Down
10 changes: 10 additions & 0 deletions commands/compile/compile.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,15 @@ func Compile(ctx context.Context, req *rpc.CompileRequest, outStream, errStream
}
}

// At the current time we do not have a way of knowing if a board supports the secure boot or not,
// so, if the flags to override the default keys are used, we try override the corresponding platform property nonetheless.
// It's not possible to use the default name for the keys since there could be more tools to sign and encrypt.
// So it's mandatory to use all three flags to sign and encrypt the binary
securityKeysOverride := []string{}
if req.KeysKeychain != "" && req.SignKey != "" && req.EncryptKey != "" {
securityKeysOverride = append(securityKeysOverride, "build.keys.keychain="+req.KeysKeychain, "build.keys.sign_key="+req.GetSignKey(), "build.keys.encrypt_key="+req.EncryptKey)
}

builderCtx := &types.Context{}
builderCtx.PackageManager = pm
builderCtx.FQBN = fqbn
Expand Down Expand Up @@ -165,6 +174,7 @@ func Compile(ctx context.Context, req *rpc.CompileRequest, outStream, errStream
builderCtx.WarningsLevel = req.GetWarnings()

builderCtx.CustomBuildProperties = append(req.GetBuildProperties(), "build.warn_data_percentage=75")
builderCtx.CustomBuildProperties = append(req.GetBuildProperties(), securityKeysOverride...)

if req.GetBuildCachePath() != "" {
builderCtx.BuildCachePath = paths.New(req.GetBuildCachePath())
Expand Down
67 changes: 67 additions & 0 deletions docs/guides/secure-boot.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Secure Boot

A ["secure boot"](https://www.keyfactor.com/blog/what-is-secure-boot-its-where-iot-security-starts/) capability may be
offered by Arduino boards platforms.

The compiled sketch is signed and encrypted by a [tool](../platform-specification.md#tools) before being flashed to the
target board. The bootloader of the board is then responsible for starting the compiled sketch only if the matching keys
are used.

To be able to correctly carry out all the operations at the end of the build we can leverage the
[post build hooks](../platform-specification.md#pre-and-post-build-hooks-since-arduino-ide-165) to sign and encrypt a
binary by using `recipe.hooks.objcopy.postobjcopy.NUMBER.pattern` key in
[`platform.txt`](../platform-specification.md#platformtxt). The security keys used are defined in the
[`boards.txt`](../platform-specification.md#boardstxt) file, this way there could be different keys for different
boards.

```
[...]
## Create secure image (bin file)
recipe.hooks.objcopy.postobjcopy.1.pattern={build.postbuild.cmd}

#
# IMGTOOL
#
tools.imgtool.cmd=imgtool
tools.imgtool.flags=sign --key "{build.keys.keychain}/{build.keys.sign_key}" --encrypt "{build.keys.keychain}/{build.keys.encrypt_key}" "{build.path}/{build.project_name}.bin" "{build.path}/{build.project_name}.bin" --align {build.alignment} --max-align {build.alignment} --version {build.version} --header-size {build.header_size} --pad-header --slot-size {build.slot_size}
[...]

```

By having only `tools.TOOL_NAME.cmd` and `tools.TOOL_NAME.flags`, we can customize the behavior with a
[custom board option](../platform-specification.md#custom-board-options). Then in the
[`boards.txt`](../platform-specification.md#boardstxt) we can define the new option to use a different
`build.postbuild.cmd`:

```
[...]
menu.security=Security setting

envie_m7.menu.security.none=None
envie_m7.menu.security.sien=Signature + Encryption

envie_m7.menu.security.sien.build.postbuild.cmd="{tools.imgtool.cmd}" {tools.imgtool.flags}
envie_m7.menu.security.none.build.postbuild.cmd="{tools.imgtool.cmd}" exit

envie_m7.menu.security.sien.build.keys.keychain={runtime.hardware.path}/Default_Keys
envie_m7.menu.security.sien.build.keys.sign_key=default-signing-key.pem
envie_m7.menu.security.sien.build.keys.encrypt_key=default-encrypt-key.pem
[...]
```

The security keys can be added with:

- `build.keys.keychain` indicates the path of the dir where to search for the custom keys to sign and encrypt a binary.
- `build.keys.sign_key` indicates the name of the custom signing key to use to sign a binary during the compile process.
- `build.keys.encrypt_key` indicates the name of the custom encryption key to use to encrypt a binary during the compile
process.

It's suggested to use the property names mentioned before, because they can be overridden respectively with
`--keys-keychain`, `--sign-key` and `--encrypt-key` Arduino CLI [compile flags](../commands/arduino-cli_compile.md).

For example, by using the following command, the sketch is compiled and the resulting binary is signed and encrypted
with the specified keys located in `/home/user/Arduino/keys` directory:

```
arduino-cli compile -b arduino:mbed_portenta:envie_m7:security=sien --keys-keychain /home/user/Arduino/keys --sign-key ecsdsa-p256-signing-key.pem --encrypt-key ecsdsa-p256-encrypt-key.pem /home/user/Arduino/MySketch
```
16 changes: 15 additions & 1 deletion docs/platform-specification.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,20 @@ the name of the architecture is set as well.

There are some other **{build.xxx}** properties available, that are explained in the boards.txt section of this guide.

#### Security credential properties

Some of them allow specifying trusted security credentials (signing and encryption keys) that can be used by a
["secure boot" system](guides/secure-boot.md):

- `build.keys.keychain`: for the directory containing the keys
- `build.keys.sign_key`: for the signing key
- `build.keys.encrypt_key`: for the encryption key

If any of these properties are defined, the others are required.

These properties can be overwritten respectively with `--keys-keychain`, `--sign-key`, `--encrypt-key`
[compile](commands/arduino-cli_compile.md) flags in the Arduino CLI.

#### Recipes to compile source code

We said that the Arduino development software determines a list of files to compile. Each file can be source code
Expand Down Expand Up @@ -1294,7 +1308,7 @@ It can sometimes be useful to provide user selectable configuration options for
could be provided in two or more variants with different microcontrollers, or may have different crystal speed based on
the board model, and so on...

When using Arduino CLI, the option can be selected via the FQBN.
When using Arduino CLI, the option can be selected via the FQBN, or using the `--board-options` flag

In the Arduino IDE the options add extra menu items under the "Tools" menu.

Expand Down
9 changes: 9 additions & 0 deletions legacy/builder/setup_build_properties.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"github.com/arduino/arduino-cli/legacy/builder/types"
properties "github.com/arduino/go-properties-orderedmap"
timeutils "github.com/arduino/go-timeutils"
"github.com/pkg/errors"
)

type SetupBuildProperties struct{}
Expand Down Expand Up @@ -126,6 +127,14 @@ func (s *SetupBuildProperties) Run(ctx *types.Context) error {

buildProperties.Merge(ctx.PackageManager.CustomGlobalProperties)

keychainProp := buildProperties.ContainsKey("build.keys.keychain")
signProp := buildProperties.ContainsKey("build.keys.sign_key")
encryptProp := buildProperties.ContainsKey("build.keys.encrypt_key")
// we verify that all the properties for the secure boot keys are defined or none of them is defined.
if (keychainProp || signProp || encryptProp) && !(keychainProp && signProp && encryptProp) {
return errors.Errorf("%s platform does not specify correctly default sign and encryption keys", targetPlatform.Platform)
}

ctx.BuildProperties = buildProperties

return nil
Expand Down
Loading