-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add GPG key verification for RPM packages
Signed-off-by: Igor Shishkin <me@teran.dev>
- Loading branch information
Showing
10 changed files
with
410 additions
and
106 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
package service | ||
|
||
import ( | ||
"bytes" | ||
"context" | ||
"io" | ||
"net/http" | ||
"os" | ||
"strings" | ||
|
||
"github.com/ProtonMail/go-crypto/openpgp" | ||
"github.com/pkg/errors" | ||
) | ||
|
||
func getGPGKey(ctx context.Context, filepath string) (openpgp.EntityList, error) { | ||
p := strings.SplitN(filepath, "://", 2) | ||
if len(p) != 2 { | ||
return nil, errors.New("unexpected public key file path format. Please use file:///path/to/file.gpg or http://example.com/file.gpg") | ||
} | ||
|
||
var data []byte | ||
switch p[0] { | ||
case "file": | ||
fp, err := os.Open(p[1]) | ||
if err != nil { | ||
return nil, errors.Wrap(err, "error opening public key file") | ||
} | ||
defer fp.Close() | ||
|
||
data, err = io.ReadAll(fp) | ||
if err != nil { | ||
return nil, errors.Wrap(err, "error reading public key file") | ||
} | ||
case "http", "https": | ||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, filepath, nil) | ||
if err != nil { | ||
return nil, errors.Wrap(err, "error constructing HTTP request object") | ||
} | ||
|
||
resp, err := http.DefaultClient.Do(req) | ||
if err != nil { | ||
return nil, errors.Wrap(err, "error performing HTTP request") | ||
} | ||
defer resp.Body.Close() | ||
|
||
data, err = io.ReadAll(resp.Body) | ||
if err != nil { | ||
return nil, errors.Wrap(err, "error reading public key data") | ||
} | ||
default: | ||
return nil, errors.Errorf("unsupported key file access scheme: `%s`", p[0]) | ||
} | ||
|
||
return openpgp.ReadArmoredKeyRing(bytes.NewReader(data)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
package service | ||
|
||
import ( | ||
"context" | ||
"net/http" | ||
"net/http/httptest" | ||
"os" | ||
"testing" | ||
|
||
"github.com/ProtonMail/go-crypto/openpgp" | ||
echo "github.com/labstack/echo/v4" | ||
"github.com/labstack/echo/v4/middleware" | ||
"github.com/stretchr/testify/require" | ||
"github.com/teran/archived/repositories/blob/mock" | ||
"github.com/teran/go-ptr" | ||
) | ||
|
||
func TestGetGPGKey(t *testing.T) { | ||
ctx := context.TODO() | ||
|
||
m := &testHandlerMock{} | ||
defer m.AssertExpectations(t) | ||
|
||
data, err := os.ReadFile("./testdata/gpg/somekey.gpg") | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
m.On("StaticFile", "/").Return(http.StatusOK, "text/plain", data).Once() | ||
|
||
e := echo.New() | ||
e.Use(middleware.Logger()) | ||
e.Use(middleware.Recover()) | ||
e.GET("/*", m.StaticFile) | ||
|
||
srv := httptest.NewServer(e) | ||
defer srv.Close() | ||
|
||
type testCase struct { | ||
name string | ||
url string | ||
expKeyIDs []uint64 | ||
expErrorText *string | ||
} | ||
|
||
tcs := []testCase{ | ||
{ | ||
name: "read from file", | ||
url: "file://./testdata/gpg/somekey.gpg", | ||
expKeyIDs: []uint64{16738405096140781972}, | ||
}, | ||
{ | ||
name: "read form HTTP URL", | ||
url: srv.URL, | ||
expKeyIDs: []uint64{16738405096140781972}, | ||
}, | ||
{ | ||
name: "incorrect scheme", | ||
expErrorText: ptr.String( | ||
"unexpected public key file path format. Please use file:///path/to/file.gpg or http://example.com/file.gpg"), | ||
}, | ||
{ | ||
name: "unknown scheme", | ||
url: "ftp://example.com/file.gpg", | ||
expErrorText: ptr.String( | ||
"unsupported key file access scheme: `ftp`"), | ||
}, | ||
} | ||
|
||
for _, tc := range tcs { | ||
t.Run(tc.name, func(t *testing.T) { | ||
r := require.New(t) | ||
|
||
keys, err := getGPGKey(ctx, tc.url) | ||
if tc.expErrorText != nil { | ||
r.Error(err) | ||
r.Equal(*tc.expErrorText, err.Error()) | ||
} else { | ||
r.NoError(err) | ||
r.Equal(func(el openpgp.EntityList) []uint64 { | ||
keyIDs := []uint64{} | ||
for _, key := range el { | ||
keyIDs = append(keyIDs, key.PrimaryKey.KeyId) | ||
} | ||
return keyIDs | ||
}(keys), tc.expKeyIDs) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
type testHandlerMock struct { | ||
mock.Mock | ||
} | ||
|
||
func (m *testHandlerMock) StaticFile(c echo.Context) error { | ||
args := m.Called(c.Request().RequestURI) | ||
return c.Blob(args.Int(0), args.String(1), args.Get(2).([]byte)) | ||
} |
Oops, something went wrong.