diff --git a/cli/arguments/arguments.go b/cli/arguments/arguments.go index 92a057986d2..1b9dfe92b3c 100644 --- a/cli/arguments/arguments.go +++ b/cli/arguments/arguments.go @@ -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) + } + } +} diff --git a/cli/compile/compile.go b/cli/compile/compile.go index db7025326ae..867f451482b 100644 --- a/cli/compile/compile.go +++ b/cli/compile/compile.go @@ -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. @@ -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.")) @@ -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() @@ -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) diff --git a/commands/compile/compile.go b/commands/compile/compile.go index 0e99add55bc..84fddd78470 100644 --- a/commands/compile/compile.go +++ b/commands/compile/compile.go @@ -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 @@ -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()) diff --git a/docs/guides/secure-boot.md b/docs/guides/secure-boot.md new file mode 100644 index 00000000000..4ccd56f9d1f --- /dev/null +++ b/docs/guides/secure-boot.md @@ -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 +``` diff --git a/docs/platform-specification.md b/docs/platform-specification.md index cc71d6810c0..b097dc4cfe6 100644 --- a/docs/platform-specification.md +++ b/docs/platform-specification.md @@ -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 @@ -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. diff --git a/legacy/builder/setup_build_properties.go b/legacy/builder/setup_build_properties.go index e102baea351..309a371752a 100644 --- a/legacy/builder/setup_build_properties.go +++ b/legacy/builder/setup_build_properties.go @@ -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{} @@ -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 diff --git a/rpc/cc/arduino/cli/commands/v1/compile.pb.go b/rpc/cc/arduino/cli/commands/v1/compile.pb.go index e283180547a..dad0be067ec 100644 --- a/rpc/cc/arduino/cli/commands/v1/compile.pb.go +++ b/rpc/cc/arduino/cli/commands/v1/compile.pb.go @@ -97,6 +97,13 @@ type CompileRequest struct { ExportBinaries *wrapperspb.BoolValue `protobuf:"bytes,23,opt,name=export_binaries,json=exportBinaries,proto3" json:"export_binaries,omitempty"` // List of paths to library root folders Library []string `protobuf:"bytes,24,rep,name=library,proto3" json:"library,omitempty"` + // The path where to search for the custom signing key name and the encrypt + // key name + KeysKeychain string `protobuf:"bytes,25,opt,name=keys_keychain,json=keysKeychain,proto3" json:"keys_keychain,omitempty"` + // The name of the custom key to use for signing during the compile process + SignKey string `protobuf:"bytes,26,opt,name=sign_key,json=signKey,proto3" json:"sign_key,omitempty"` + // The name of the custom key to use for encrypting during the compile process + EncryptKey string `protobuf:"bytes,27,opt,name=encrypt_key,json=encryptKey,proto3" json:"encrypt_key,omitempty"` } func (x *CompileRequest) Reset() { @@ -278,6 +285,27 @@ func (x *CompileRequest) GetLibrary() []string { return nil } +func (x *CompileRequest) GetKeysKeychain() string { + if x != nil { + return x.KeysKeychain + } + return "" +} + +func (x *CompileRequest) GetSignKey() string { + if x != nil { + return x.SignKey + } + return "" +} + +func (x *CompileRequest) GetEncryptKey() string { + if x != nil { + return x.EncryptKey + } + return "" +} + type CompileResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -466,7 +494,7 @@ var file_cc_arduino_cli_commands_v1_compile_proto_rawDesc = []byte{ 0x76, 0x31, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x24, 0x63, 0x63, 0x2f, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2f, 0x63, 0x6c, 0x69, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x6c, 0x69, 0x62, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x92, 0x07, 0x0a, 0x0e, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xf3, 0x07, 0x0a, 0x0e, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x40, 0x0a, 0x08, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, @@ -519,56 +547,62 @@ var file_cc_arduino_cli_commands_v1_compile_proto_rawDesc = []byte{ 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0e, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x42, 0x69, 0x6e, 0x61, 0x72, 0x69, 0x65, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x18, 0x18, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, - 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x1a, 0x41, 0x0a, 0x13, 0x53, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, - 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, - 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x99, 0x04, 0x0a, 0x0f, 0x43, - 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, - 0x0a, 0x0a, 0x6f, 0x75, 0x74, 0x5f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x09, 0x6f, 0x75, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1d, 0x0a, - 0x0a, 0x65, 0x72, 0x72, 0x5f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x09, 0x65, 0x72, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1d, 0x0a, 0x0a, - 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x09, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x50, 0x61, 0x74, 0x68, 0x12, 0x4a, 0x0a, 0x0e, 0x75, - 0x73, 0x65, 0x64, 0x5f, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x69, 0x65, 0x73, 0x18, 0x04, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, + 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x12, 0x23, 0x0a, 0x0d, 0x6b, 0x65, 0x79, 0x73, 0x5f, + 0x6b, 0x65, 0x79, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x18, 0x19, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, + 0x6b, 0x65, 0x79, 0x73, 0x4b, 0x65, 0x79, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x12, 0x19, 0x0a, 0x08, + 0x73, 0x69, 0x67, 0x6e, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x1a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, + 0x73, 0x69, 0x67, 0x6e, 0x4b, 0x65, 0x79, 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x6e, 0x63, 0x72, 0x79, + 0x70, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x1b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, 0x6e, + 0x63, 0x72, 0x79, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x1a, 0x41, 0x0a, 0x13, 0x53, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, + 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, + 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x99, 0x04, 0x0a, 0x0f, + 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x1d, 0x0a, 0x0a, 0x6f, 0x75, 0x74, 0x5f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x09, 0x6f, 0x75, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1d, + 0x0a, 0x0a, 0x65, 0x72, 0x72, 0x5f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x09, 0x65, 0x72, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1d, 0x0a, + 0x0a, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x09, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x50, 0x61, 0x74, 0x68, 0x12, 0x4a, 0x0a, 0x0e, + 0x75, 0x73, 0x65, 0x64, 0x5f, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x69, 0x65, 0x73, 0x18, 0x04, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, + 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, + 0x31, 0x2e, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x52, 0x0d, 0x75, 0x73, 0x65, 0x64, 0x4c, + 0x69, 0x62, 0x72, 0x61, 0x72, 0x69, 0x65, 0x73, 0x12, 0x6b, 0x0a, 0x18, 0x65, 0x78, 0x65, 0x63, + 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x73, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x5f, + 0x73, 0x69, 0x7a, 0x65, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x63, 0x63, 0x2e, + 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, + 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x61, 0x62, + 0x6c, 0x65, 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x69, 0x7a, 0x65, 0x52, 0x16, 0x65, + 0x78, 0x65, 0x63, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x54, 0x0a, 0x0e, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x5f, 0x70, + 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, + 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, + 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x6c, 0x61, 0x74, 0x66, + 0x6f, 0x72, 0x6d, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x0d, 0x62, 0x6f, + 0x61, 0x72, 0x64, 0x50, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x12, 0x54, 0x0a, 0x0e, 0x62, + 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x18, 0x07, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, - 0x2e, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x52, 0x0d, 0x75, 0x73, 0x65, 0x64, 0x4c, 0x69, - 0x62, 0x72, 0x61, 0x72, 0x69, 0x65, 0x73, 0x12, 0x6b, 0x0a, 0x18, 0x65, 0x78, 0x65, 0x63, 0x75, - 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x73, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x5f, 0x73, - 0x69, 0x7a, 0x65, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x63, 0x63, 0x2e, 0x61, - 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, - 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x61, 0x62, 0x6c, - 0x65, 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x69, 0x7a, 0x65, 0x52, 0x16, 0x65, 0x78, - 0x65, 0x63, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x53, 0x69, 0x7a, 0x65, 0x12, 0x54, 0x0a, 0x0e, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x5f, 0x70, 0x6c, - 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x63, - 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, - 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x6c, 0x61, 0x74, 0x66, 0x6f, - 0x72, 0x6d, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x0d, 0x62, 0x6f, 0x61, - 0x72, 0x64, 0x50, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x12, 0x54, 0x0a, 0x0e, 0x62, 0x75, - 0x69, 0x6c, 0x64, 0x5f, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x18, 0x07, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, - 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, - 0x50, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, - 0x65, 0x52, 0x0d, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x50, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, - 0x12, 0x44, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x18, 0x08, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, - 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, - 0x54, 0x61, 0x73, 0x6b, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x52, 0x08, 0x70, 0x72, - 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x22, 0x5a, 0x0a, 0x15, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, - 0x61, 0x62, 0x6c, 0x65, 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x69, 0x7a, 0x65, 0x12, - 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, - 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x03, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x6d, 0x61, 0x78, 0x5f, 0x73, - 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x6d, 0x61, 0x78, 0x53, 0x69, - 0x7a, 0x65, 0x42, 0x48, 0x5a, 0x46, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, - 0x2f, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2f, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, - 0x2d, 0x63, 0x6c, 0x69, 0x2f, 0x72, 0x70, 0x63, 0x2f, 0x63, 0x63, 0x2f, 0x61, 0x72, 0x64, 0x75, - 0x69, 0x6e, 0x6f, 0x2f, 0x63, 0x6c, 0x69, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, - 0x2f, 0x76, 0x31, 0x3b, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x62, 0x06, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x33, + 0x2e, 0x50, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, + 0x63, 0x65, 0x52, 0x0d, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x50, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, + 0x6d, 0x12, 0x44, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x18, 0x08, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, + 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, + 0x2e, 0x54, 0x61, 0x73, 0x6b, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x52, 0x08, 0x70, + 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x22, 0x5a, 0x0a, 0x15, 0x45, 0x78, 0x65, 0x63, 0x75, + 0x74, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x69, 0x7a, 0x65, + 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x6d, 0x61, 0x78, 0x5f, + 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x6d, 0x61, 0x78, 0x53, + 0x69, 0x7a, 0x65, 0x42, 0x48, 0x5a, 0x46, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2f, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, + 0x6f, 0x2d, 0x63, 0x6c, 0x69, 0x2f, 0x72, 0x70, 0x63, 0x2f, 0x63, 0x63, 0x2f, 0x61, 0x72, 0x64, + 0x75, 0x69, 0x6e, 0x6f, 0x2f, 0x63, 0x6c, 0x69, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, + 0x73, 0x2f, 0x76, 0x31, 0x3b, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/rpc/cc/arduino/cli/commands/v1/compile.proto b/rpc/cc/arduino/cli/commands/v1/compile.proto index 2288675cb4e..2164aec36d8 100644 --- a/rpc/cc/arduino/cli/commands/v1/compile.proto +++ b/rpc/cc/arduino/cli/commands/v1/compile.proto @@ -80,6 +80,13 @@ message CompileRequest { google.protobuf.BoolValue export_binaries = 23; // List of paths to library root folders repeated string library = 24; + // The path where to search for the custom signing key name and the encrypt + // key name + string keys_keychain = 25; + // The name of the custom key to use for signing during the compile process + string sign_key = 26; + // The name of the custom key to use for encrypting during the compile process + string encrypt_key = 27; } message CompileResponse { diff --git a/test/test_compile_part_4.py b/test/test_compile_part_4.py index d2c16a64869..bdc85d8cb9e 100644 --- a/test/test_compile_part_4.py +++ b/test/test_compile_part_4.py @@ -415,3 +415,87 @@ def test_compile_with_known_platform_not_installed(run_command, data_dir): assert "Error during build: Platform 'arduino:avr' not found: platform not installed" in res.stderr # Verifies command to fix error is shown to user assert "Try running `arduino-cli core install arduino:avr`" in res.stderr + + +def test_compile_with_fake_secure_boot_core(run_command, data_dir): + assert run_command(["update"]) + + assert run_command(["core", "install", "arduino:avr@1.8.3"]) + + sketch_name = "SketchSimple" + sketch_path = Path(data_dir, sketch_name) + fqbn = "arduino:avr:uno" + + assert run_command(["sketch", "new", sketch_path]) + + # Verifies compilation works + assert run_command(["compile", "--clean", "-b", fqbn, sketch_path]) + + # Overrides default platform adding secure_boot support using platform.local.txt + avr_platform_path = Path(data_dir, "packages", "arduino", "hardware", "avr", "1.8.3", "platform.local.txt") + test_platform_name = "platform_with_secure_boot" + shutil.copyfile( + Path(__file__).parent / "testdata" / test_platform_name / "platform.local.txt", + avr_platform_path, + ) + + # Overrides default board adding secure boot support using board.local.txt + avr_board_path = Path(data_dir, "packages", "arduino", "hardware", "avr", "1.8.3", "boards.local.txt") + shutil.copyfile( + Path(__file__).parent / "testdata" / test_platform_name / "boards.local.txt", + avr_board_path, + ) + + # Verifies compilation works with secure boot disabled + res = run_command(["compile", "--clean", "-b", fqbn + ":security=none", sketch_path, "-v"]) + assert res.ok + assert "echo exit" in res.stdout + + # Verifies compilation works with secure boot enabled + res = run_command(["compile", "--clean", "-b", fqbn + ":security=sien", sketch_path, "-v"]) + assert res.ok + assert "Default_Keys/default-signing-key.pem" in res.stdout + assert "Default_Keys/default-encrypt-key.pem" in res.stdout + + # Verifies compilation does not work with secure boot enabled and using only one flag + res = run_command( + [ + "compile", + "--clean", + "-b", + fqbn + ":security=sien", + sketch_path, + "--keys-keychain", + data_dir, + "-v", + ] + ) + assert res.failed + assert "Flag --sign-key is mandatory when used in conjunction with flag --keys-keychain" in res.stderr + + # Verifies compilation works with secure boot enabled and when overriding the sign key and encryption key used + keys_dir = Path(data_dir, "keys_dir") + keys_dir.mkdir() + sign_key_path = Path(keys_dir, "my-sign-key.pem") + sign_key_path.touch() + encrypt_key_path = Path(keys_dir, "my-encrypt-key.pem") + encrypt_key_path.touch() + res = run_command( + [ + "compile", + "--clean", + "-b", + fqbn + ":security=sien", + sketch_path, + "--keys-keychain", + keys_dir, + "--sign-key", + "my-sign-key.pem", + "--encrypt-key", + "my-encrypt-key.pem", + "-v", + ] + ) + assert res.ok + assert "my-sign-key.pem" in res.stdout + assert "my-encrypt-key.pem" in res.stdout diff --git a/test/testdata/platform_with_secure_boot/boards.local.txt b/test/testdata/platform_with_secure_boot/boards.local.txt new file mode 100644 index 00000000000..e1d30e681b6 --- /dev/null +++ b/test/testdata/platform_with_secure_boot/boards.local.txt @@ -0,0 +1,11 @@ +menu.security=Security setting + +uno.menu.security.none=None +uno.menu.security.sien=Signature + Encryption + +uno.menu.security.sien.build.postbuild.cmd="{tools.imgtool.cmd}" {tools.imgtool.flags} +uno.menu.security.none.build.postbuild.cmd="{tools.imgtool.cmd}" exit + +uno.menu.security.sien.build.keys.keychain={runtime.hardware.path}/Default_Keys +uno.menu.security.sien.build.keys.sign_key=default-signing-key.pem +uno.menu.security.sien.build.keys.encrypt_key=default-encrypt-key.pem diff --git a/test/testdata/platform_with_secure_boot/platform.local.txt b/test/testdata/platform_with_secure_boot/platform.local.txt new file mode 100644 index 00000000000..320de131bf0 --- /dev/null +++ b/test/testdata/platform_with_secure_boot/platform.local.txt @@ -0,0 +1,8 @@ +## Create output secure image (bin file) +recipe.hooks.objcopy.postobjcopy.1.pattern={build.postbuild.cmd} +# +# IMGTOOL +# + +tools.imgtool.cmd=echo +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} \ No newline at end of file