Skip to content

Commit 7eb9c57

Browse files
authored
Implement function to verify index signature with custom key (#1304)
* add gpg public key to verify board_index.json * add new function to allow the usage of another gpg key * add new test and enhance existing ones * apply suggestions by @cmaglie * move `module_firmware_index.gpg.key` under `testdata/`
1 parent cdbebe9 commit 7eb9c57

5 files changed

+505
-4
lines changed

arduino/security/signature_test.go

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,36 @@ import (
2222
"github.com/stretchr/testify/require"
2323
)
2424

25-
func TestSignatureVerification(t *testing.T) {
26-
res, signer, err := VerifyArduinoDetachedSignature(paths.New("testdata/package_index.json"), paths.New("testdata/package_index.json.sig"))
25+
var (
26+
PackageIndexPath = paths.New("testdata/package_index.json")
27+
PackageSignaturePath = paths.New("testdata/package_index.json.sig")
28+
BoardIndexPath = paths.New("testdata/module_firmware_index.json")
29+
BoardSignaturePath = paths.New("testdata/module_firmware_index.json.sig")
30+
BoardKey = paths.New("testdata/module_firmware_index_public.gpg.key")
31+
InvalidIndexPath = paths.New("testdata/invalid_file.json")
32+
)
33+
34+
func TestVerifyArduinoDetachedSignature(t *testing.T) {
35+
res, signer, err := VerifyArduinoDetachedSignature(PackageIndexPath, PackageSignaturePath)
2736
require.NoError(t, err)
2837
require.NotNil(t, signer)
2938
require.True(t, res)
3039
require.Equal(t, uint64(0x7baf404c2dfab4ae), signer.PrimaryKey.KeyId)
3140

32-
res, signer, err = VerifyArduinoDetachedSignature(paths.New("testdata/invalid_file.json"), paths.New("testdata/package_index.json.sig"))
41+
res, signer, err = VerifyArduinoDetachedSignature(InvalidIndexPath, PackageSignaturePath)
42+
require.False(t, res)
43+
require.Nil(t, signer)
44+
require.Error(t, err)
45+
}
46+
47+
func TestVerifyDetachedSignature(t *testing.T) {
48+
res, signer, err := VerifyDetachedSignature(BoardIndexPath, BoardSignaturePath, BoardKey)
49+
require.NoError(t, err)
50+
require.NotNil(t, signer)
51+
require.True(t, res)
52+
require.Equal(t, uint64(0x82f2d7c7c5a22a73), signer.PrimaryKey.KeyId)
53+
54+
res, signer, err = VerifyDetachedSignature(InvalidIndexPath, PackageSignaturePath, BoardKey)
3355
require.False(t, res)
3456
require.Nil(t, signer)
3557
require.Error(t, err)

arduino/security/signatures.go

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ package security
1717

1818
import (
1919
"fmt"
20+
"io"
21+
"os"
2022

2123
"github.com/arduino/go-paths-helper"
2224
rice "github.com/cmaglie/go.rice"
@@ -37,11 +39,33 @@ func VerifyArduinoDetachedSignature(targetPath *paths.Path, signaturePath *paths
3739
if err != nil {
3840
panic("could not find bundled signature keys")
3941
}
42+
return verifySignature(targetPath, signaturePath, arduinoKeyringFile)
43+
}
44+
45+
// VerifyDetachedSignature checks that the detached GPG signature (in the
46+
// signaturePath file) matches the given targetPath file and is an authentic
47+
// signature from the bundled trusted keychain. The keyPath is the path of the public key used.
48+
// This function allows to specify the path of the key to use.
49+
// If any of the above conditions fails this function returns false.
50+
// The PGP entity in the trusted keychain that produced the signature is returned too.
51+
func VerifyDetachedSignature(targetPath *paths.Path, signaturePath *paths.Path, keyPath *paths.Path) (bool, *openpgp.Entity, error) {
52+
arduinoKeyringFile, err := os.Open(keyPath.String())
53+
if err != nil {
54+
panic("could not open signature keys")
55+
}
56+
defer arduinoKeyringFile.Close()
57+
return verifySignature(targetPath, signaturePath, arduinoKeyringFile)
58+
}
59+
60+
//verifySignature is an helper function that checks that the detached GPG signature (in the
61+
// signaturePath file) matches the given targetPath file and is an authentic
62+
// signature. If any of the above conditions fails this function returns false.
63+
// The PGP entity in the trusted keychain that produced the signature is returned too.
64+
func verifySignature(targetPath *paths.Path, signaturePath *paths.Path, arduinoKeyringFile io.Reader) (bool, *openpgp.Entity, error) {
4065
keyRing, err := openpgp.ReadKeyRing(arduinoKeyringFile)
4166
if err != nil {
4267
return false, nil, fmt.Errorf("retrieving Arduino public keys: %s", err)
4368
}
44-
4569
target, err := targetPath.Open()
4670
if err != nil {
4771
return false, nil, fmt.Errorf("opening target file: %s", err)

0 commit comments

Comments
 (0)