diff --git a/cli/certificates/certificates.go b/cli/certificates/certificates.go new file mode 100644 index 00000000..9d06305d --- /dev/null +++ b/cli/certificates/certificates.go @@ -0,0 +1,38 @@ +/* + FirmwareUploader + Copyright (c) 2021 Arduino LLC. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +package certificates + +import ( + "os" + + "github.com/spf13/cobra" +) + +func NewCommand() *cobra.Command { + firmwareCmd := &cobra.Command{ + Use: "certificates", + Short: "Commands to operate on certificates.", + Long: "A subset of commands to perform various certificates operations.", + Example: " " + os.Args[0] + " certificates ...", + } + + firmwareCmd.AddCommand(NewFlashCommand()) + return firmwareCmd +} diff --git a/cli/certificates/flash.go b/cli/certificates/flash.go new file mode 100644 index 00000000..421209f9 --- /dev/null +++ b/cli/certificates/flash.go @@ -0,0 +1,183 @@ +/* + FirmwareUploader + Copyright (c) 2021 Arduino LLC. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +package certificates + +import ( + "bytes" + "os" + "path/filepath" + "strings" + "time" + + "github.com/arduino/FirmwareUploader/flasher" + "github.com/arduino/FirmwareUploader/indexes" + "github.com/arduino/FirmwareUploader/indexes/download" + programmer "github.com/arduino/FirmwareUploader/programmers" + "github.com/arduino/arduino-cli/arduino/serialutils" + "github.com/arduino/arduino-cli/cli/errorcodes" + "github.com/arduino/arduino-cli/cli/feedback" + "github.com/arduino/go-paths-helper" + "github.com/arduino/go-properties-orderedmap" + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" +) + +var ( + fqbn string + address string + certificateURLs []string + certificatePaths []string +) + +// NewCommand created a new `version` command +func NewFlashCommand() *cobra.Command { + command := &cobra.Command{ + Use: "flash", + Short: "Flashes certificates to board.", + Long: "Flashes specified certificates to board at specified address.", + Example: "" + + " " + os.Args[0] + " flash --fqbn arduino:samd:mkr1000 --address COM10 --url arduino.cc:443 --file /home/me/Digicert.cer\n" + + " " + os.Args[0] + " flash -b arduino:samd:mkr1000 -a COM10 -u arduino.cc:443 -u google.cc:443\n" + + " " + os.Args[0] + " flash -b arduino:samd:mkr1000 -a COM10 -f /home/me/VeriSign.cer -f /home/me/Digicert.cer\n", + Args: cobra.NoArgs, + Run: run, + } + + command.Flags().StringVarP(&fqbn, "fqbn", "b", "", "Fully Qualified Board Name, e.g.: arduino:samd:mkr1000, arduino:mbed_nano:nanorp2040connect") + command.Flags().StringVarP(&address, "address", "a", "", "Upload port, e.g.: COM10, /dev/ttyACM0") + command.Flags().StringSliceVarP(&certificateURLs, "url", "u", []string{}, "List of urls to download root certificates, e.g.: arduino.cc:443") + command.Flags().StringSliceVarP(&certificatePaths, "file", "f", []string{}, "List of paths to certificate file, e.g.: /home/me/Digicert.cer") + return command +} + +func run(cmd *cobra.Command, args []string) { + packageIndex, err := indexes.GetPackageIndex() + if err != nil { + feedback.Errorf("Can't load package index: %s", err) + os.Exit(errorcodes.ErrGeneric) + } + + firmwareIndex, err := indexes.GetFirmwareIndex() + if err != nil { + feedback.Errorf("Can't load firmware index: %s", err) + os.Exit(errorcodes.ErrGeneric) + } + + if fqbn == "" { + feedback.Errorf("Error during certificates flashing: missing board fqbn") + os.Exit(errorcodes.ErrBadArgument) + } + + if address == "" { + feedback.Errorf("Error during certificates flashing: missing board address") + os.Exit(errorcodes.ErrBadArgument) + } + + if len(certificateURLs) == 0 && len(certificatePaths) == 0 { + feedback.Errorf("Error during certificates flashing: no certificates provided") + os.Exit(errorcodes.ErrBadArgument) + } + + board := firmwareIndex.GetBoard(fqbn) + if board == nil { + feedback.Errorf("Can't find board with %s fqbn", fqbn) + os.Exit(errorcodes.ErrBadArgument) + } + + toolRelease := indexes.GetToolRelease(packageIndex, board.Uploader) + if toolRelease == nil { + feedback.Errorf("Error getting upload tool %s for board %s", board.Uploader, board.Fqbn) + os.Exit(errorcodes.ErrGeneric) + } + uploadToolDir, err := download.DownloadTool(toolRelease) + if err != nil { + feedback.Errorf("Error downloading tool %s: %s", board.Uploader, err) + os.Exit(errorcodes.ErrGeneric) + } + + loaderSketchPath, err := download.DownloadLoaderSketch(board.LoaderSketch) + if err != nil { + feedback.Errorf("Error downloading loader sketch from %s: %s", board.LoaderSketch.URL, err) + os.Exit(errorcodes.ErrGeneric) + } + + loaderSketch := strings.ReplaceAll(loaderSketchPath.String(), loaderSketchPath.Ext(), "") + + uploaderCommand := board.GetUploaderCommand() + uploaderCommand = strings.ReplaceAll(uploaderCommand, "{tool_dir}", filepath.FromSlash(uploadToolDir.String())) + uploaderCommand = strings.ReplaceAll(uploaderCommand, "{serial.port.file}", address) + uploaderCommand = strings.ReplaceAll(uploaderCommand, "{loader.sketch}", loaderSketch) + + commandLine, err := properties.SplitQuotedString(uploaderCommand, "\"", false) + if err != nil { + feedback.Errorf(`Error splitting command line "%s": %s`, uploaderCommand, err) + os.Exit(errorcodes.ErrGeneric) + } + + // Check if board needs a 1200bps touch for upload + if board.UploadTouch { + logrus.Info("Putting board into bootloader mode") + _, err := serialutils.Reset(address, board.UploadWait, nil) + if err != nil { + feedback.Errorf("Error during certificates flashing: missing board address") + os.Exit(errorcodes.ErrGeneric) + } + } + + // Flash loader Sketch + flashOut := new(bytes.Buffer) + flashErr := new(bytes.Buffer) + // var err error + if feedback.GetFormat() == feedback.JSON { + err = programmer.Flash(commandLine, flashOut, flashErr) + } else { + err = programmer.Flash(commandLine, os.Stdout, os.Stderr) + } + if err != nil { + feedback.Errorf("Error during certificates flashing: %s", err) + os.Exit(errorcodes.ErrGeneric) + } + + // Wait a bit after flashing the loader sketch for the board to become + // available again. + time.Sleep(1 * time.Second) + + // Get flasher depending on which module to use + var f flasher.Flasher + switch board.Module { + case "NINA": + f, err = flasher.NewNinaFlasher(address) + case "SARA": + f, err = flasher.NewSaraFlasher(address) + case "WINC": + f, err = flasher.NewWincFlasher(address) + } + if err != nil { + feedback.Errorf("Error during certificates flashing: %s", err) + os.Exit(errorcodes.ErrGeneric) + } + defer f.Close() + + certFileList := paths.NewPathList(certificatePaths...) + if err := f.FlashCertificates(&certFileList, certificateURLs); err != nil { + feedback.Errorf("Error during certificates flashing: %s", err) + os.Exit(errorcodes.ErrGeneric) + } +} diff --git a/cli/cli.go b/cli/cli.go index 17b89ece..2fa31b02 100644 --- a/cli/cli.go +++ b/cli/cli.go @@ -28,6 +28,7 @@ import ( "strings" "time" + "github.com/arduino/FirmwareUploader/cli/certificates" "github.com/arduino/FirmwareUploader/cli/firmware" "github.com/arduino/FirmwareUploader/cli/version" "github.com/arduino/FirmwareUploader/modules/nina" @@ -67,8 +68,8 @@ func NewCommand() *cobra.Command { } rootCmd.AddCommand(version.NewCommand()) - rootCmd.AddCommand(firmware.NewCommand()) + rootCmd.AddCommand(certificates.NewCommand()) rootCmd.Flags().StringVar(&ctx.PortName, "port", "", "serial port to use for flashing") rootCmd.Flags().StringVar(&ctx.RootCertDir, "certs", "", "root certificate directory")