From d945078fb1beffcb762070b756b388b4b08f0004 Mon Sep 17 00:00:00 2001 From: Alessio Perugini Date: Thu, 31 Aug 2023 09:36:44 +0200 Subject: [PATCH] Fix wrong scrape of Root Certificates (#216) * Correctly lookup for RootCA * update docs * Scrape all possible root certificate In the uno r4 we're using mbedtls which has a strange behaviour. And some root certificates won't work. Therfore the most simple solution is using all the possible ones, found during the handshake. --- certificates/certutils.go | 19 ++++++++++--------- certificates/certutils_test.go | 16 ++++++++++++++++ cli/certificates/flash.go | 4 ++-- docs/plugins.md | 7 ++----- 4 files changed, 30 insertions(+), 16 deletions(-) create mode 100644 certificates/certutils_test.go diff --git a/certificates/certutils.go b/certificates/certutils.go index bd272d6..d48b9a3 100644 --- a/certificates/certutils.go +++ b/certificates/certutils.go @@ -30,7 +30,7 @@ import ( // ScrapeRootCertificatesFromURL downloads from a webserver the root certificate // required to connect to that server from the TLS handshake response. -func ScrapeRootCertificatesFromURL(URL string) (*x509.Certificate, error) { +func ScrapeRootCertificatesFromURL(URL string) ([]*x509.Certificate, error) { conn, err := tls.Dial("tcp", URL, &tls.Config{ InsecureSkipVerify: false, }) @@ -45,15 +45,16 @@ func ScrapeRootCertificatesFromURL(URL string) (*x509.Certificate, error) { return nil, err } - peerCertificates := conn.ConnectionState().PeerCertificates - if len(peerCertificates) == 0 { - err = fmt.Errorf("no peer certificates found at %s", URL) - logrus.Error(err) - return nil, err + chains := conn.ConnectionState().VerifiedChains + if len(chains) == 0 { + return nil, fmt.Errorf("no certificates found at %s", URL) } - - rootCertificate := peerCertificates[len(peerCertificates)-1] - return rootCertificate, nil + rootCertificates := make([]*x509.Certificate, len(chains)) + for i, chain := range chains { + // The last certificate of the chain is always the Root Certificate + rootCertificates[i] = chain[len(chain)-1] + } + return rootCertificates, nil } // LoadCertificatesFromFile read certificates from the given file. PEM and CER formats diff --git a/certificates/certutils_test.go b/certificates/certutils_test.go new file mode 100644 index 0000000..86e2821 --- /dev/null +++ b/certificates/certutils_test.go @@ -0,0 +1,16 @@ +package certificates_test + +import ( + "testing" + + "github.com/arduino/arduino-fwuploader/certificates" + "github.com/stretchr/testify/require" +) + +func TestScrapeRootCertificatesFromURL(t *testing.T) { + rootCerts, err := certificates.ScrapeRootCertificatesFromURL("www.arduino.cc:443") + require.NoError(t, err) + for _, cert := range rootCerts { + require.Equal(t, cert.Issuer, cert.Subject) + } +} diff --git a/cli/certificates/flash.go b/cli/certificates/flash.go index 0ca00c0..85906c3 100644 --- a/cli/certificates/flash.go +++ b/cli/certificates/flash.go @@ -121,11 +121,11 @@ func flashCertificates(uploader *plugin.FwUploader, certificateURLs, certificate for _, URL := range certificateURLs { logrus.Infof("Converting and flashing certificate from %s", URL) stdout.Write([]byte(fmt.Sprintf("Converting and flashing certificate from %s\n", URL))) - rootCert, err := certificates.ScrapeRootCertificatesFromURL(URL) + rootCerts, err := certificates.ScrapeRootCertificatesFromURL(URL) if err != nil { return nil, err } - allCerts = append(allCerts, rootCert) + allCerts = append(allCerts, rootCerts...) } f, err := certsBundle.Create() diff --git a/docs/plugins.md b/docs/plugins.md index de2fb8f..6379ad8 100644 --- a/docs/plugins.md +++ b/docs/plugins.md @@ -94,11 +94,8 @@ Error: reboot mode: upload commands sketch: setting DTR to OFF #### I flashed the certificates, but I am unable to reach the host -The **whole certificate chain** is needed to make it work. Using -[`-u` flags](commands/arduino-fwuploader_certificates_flash.md#options) (ex: `-u www.arduino.cc:443`) won’t work because -it only downloads the root certificates. The solution is to use only the -[`-f` flag](commands/arduino-fwuploader_certificates_flash.md#options) and provide a pem certificate containing the -whole chain. +There was a bug in the arduino-fwuploader prior `2.4.1` which didn't pick the actual root certificate. Upgrading to the +latest version solves the problem. #### My antivirus says that `espflash` is a threat