Skip to content

Commit

Permalink
fix(deb): dpkg-sig format (#841)
Browse files Browse the repository at this point in the history
* fix dpkg sig

* add tests

* fix imports

---------

Co-authored-by: k.safin@npo-echelon.ru <k.safin@npo-echelon.ru>
  • Loading branch information
KSpaceer and k.safin@npo-echelon.ru authored Jul 9, 2024
1 parent 62ef500 commit b3e7ff9
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 13 deletions.
18 changes: 12 additions & 6 deletions deb/deb.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"compress/gzip"
"crypto/md5" // nolint:gas
"crypto/sha1"
"encoding/hex"
"errors"
"fmt"
"io"
Expand Down Expand Up @@ -230,7 +231,9 @@ Signer: {{ .Signer }}
Date: {{ .Date }}
Role: {{ .Role }}
Files:
{{range .Files}}{{ .Md5Sum }} {{ .Sha1Sum }} {{ .Size }} {{ .Name }}{{end}}
{{range .Files -}}
{{"\t"}}{{ hex .Md5Sum }} {{ hex .Sha1Sum }} {{ .Size }} {{ .Name }}
{{end -}}
`

type dpkgSigData struct {
Expand All @@ -241,17 +244,18 @@ type dpkgSigData struct {
Info *nfpm.Info
}
type dpkgSigFileLine struct {
Md5Sum [16]byte
Sha1Sum [20]byte
Md5Sum []byte
Sha1Sum []byte
Size int
Name string
}

func newDpkgSigFileLine(name string, fileContent []byte) dpkgSigFileLine {
md5Sum, sha1Sum := md5.Sum(fileContent), sha1.Sum(fileContent)
return dpkgSigFileLine{
Name: name,
Md5Sum: md5.Sum(fileContent),
Sha1Sum: sha1.Sum(fileContent),
Md5Sum: md5Sum[:],
Sha1Sum: sha1Sum[:],
Size: len(fileContent),
}
}
Expand All @@ -267,7 +271,9 @@ func readDpkgSigData(info *nfpm.Info, debianBinary, controlTarGz, dataTarball []
newDpkgSigFileLine("data.tar.gz", dataTarball),
},
}
temp, _ := template.New("dpkg-sig").Parse(dpkgSigTemplate)
temp, _ := template.New("dpkg-sig").Funcs(template.FuncMap{
"hex": hex.EncodeToString,
}).Parse(dpkgSigTemplate)
buf := &bytes.Buffer{}
err := temp.Execute(buf, data)
if err != nil {
Expand Down
72 changes: 70 additions & 2 deletions deb/deb_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"bytes"
"compress/gzip"
"crypto/md5" // nolint: gosec
"crypto/sha1"
"encoding/hex"
"errors"
"flag"
Expand All @@ -13,6 +14,7 @@ import (
"os"
"path"
"path/filepath"
"slices"
"strconv"
"strings"
"testing"
Expand Down Expand Up @@ -1001,8 +1003,10 @@ func TestDpkgSigSignature(t *testing.T) {

signature := extractFileFromAr(t, deb.Bytes(), "_gpgbuilder")

err = sign.PGPReadMessage(signature, "../internal/sign/testdata/pubkey.asc")
msg, err := sign.PGPReadMessage(signature, "../internal/sign/testdata/pubkey.asc")
require.NoError(t, err)

require.NoError(t, verifyDpkgSigFileHashes(extractAllFilesFromAr(t, deb.Bytes()), string(msg)))
}

func TestDpkgSigSignatureError(t *testing.T) {
Expand Down Expand Up @@ -1032,8 +1036,10 @@ func TestDpkgSigSignatureCallback(t *testing.T) {

signature := extractFileFromAr(t, deb.Bytes(), "_gpgbuilder")

err = sign.PGPReadMessage(signature, "../internal/sign/testdata/pubkey.asc")
msg, err := sign.PGPReadMessage(signature, "../internal/sign/testdata/pubkey.asc")
require.NoError(t, err)

require.NoError(t, verifyDpkgSigFileHashes(extractAllFilesFromAr(t, deb.Bytes()), string(msg)))
}

func TestDisableGlobbing(t *testing.T) {
Expand Down Expand Up @@ -1400,6 +1406,26 @@ func extractFileFromAr(tb testing.TB, arFile []byte, filename string) []byte {
return nil
}

func extractAllFilesFromAr(tb testing.TB, arFile []byte) map[string][]byte {
tb.Helper()

tr := ar.NewReader(bytes.NewReader(arFile))
files := make(map[string][]byte)
for {
hdr, err := tr.Next()
if errors.Is(err, io.EOF) {
break // End of archive
}
require.NoError(tb, err)

fileContents, err := io.ReadAll(tr)
require.NoError(tb, err)

files[hdr.Name] = fileContents
}
return files
}

func TestEmptyButRequiredDebFields(t *testing.T) {
item := nfpm.WithDefaults(&nfpm.Info{
Name: "foo",
Expand Down Expand Up @@ -1506,3 +1532,45 @@ func (zrc *zstdReadCloser) Close() error {

return nil
}

func verifyDpkgSigFileHashes(arFiles map[string][]byte, msg string) error {
_, hashes, ok := strings.Cut(msg, "Files:")
if !ok {
return errors.New("expected Files section in dpkg-sig message")
}
lines := strings.Split(hashes, "\n")
for i := range lines {
lines[i] = strings.TrimSpace(lines[i])
if lines[i] == "" {
continue
}
var md5Hex, sha1Hex, size, name string
if n, err := fmt.Sscanln(lines[i], &md5Hex, &sha1Hex, &size, &name); err != nil {
return err
} else if n != 4 {
return fmt.Errorf("expected 4 elements in line %q, but got %d", lines[i], n)
}

md5Sum, err := hex.DecodeString(md5Hex)
if err != nil {
return err
}
sha1Sum, err := hex.DecodeString(sha1Hex)
if err != nil {
return err
}

content, ok := arFiles[name]
if !ok {
return fmt.Errorf("dpkg-sig message contains hash of file %q, but the package does not contain the file", name)
}
actualMD5Sum, actualSHA1Sum := md5.Sum(content), sha1.Sum(content)
if !slices.Equal(actualMD5Sum[:], md5Sum) {
return fmt.Errorf("file %q has invalid MD5 sum", name)
}
if !slices.Equal(actualSHA1Sum[:], sha1Sum) {
return fmt.Errorf("file %q has invalid SHA1 sum", name)
}
}
return nil
}
10 changes: 5 additions & 5 deletions internal/sign/pgp.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,30 +143,30 @@ func PGPVerify(message io.Reader, signature []byte, armoredPubKeyFile string) er
return err
}

func PGPReadMessage(message []byte, armoredPubKeyFile string) error {
func PGPReadMessage(message []byte, armoredPubKeyFile string) (plaintext []byte, err error) {
keyFileContent, err := os.ReadFile(armoredPubKeyFile)
if err != nil {
return fmt.Errorf("reading armored public key file: %w", err)
return nil, fmt.Errorf("reading armored public key file: %w", err)
}

var keyring openpgp.EntityList

if isASCII(keyFileContent) {
keyring, err = openpgp.ReadArmoredKeyRing(bytes.NewReader(keyFileContent))
if err != nil {
return fmt.Errorf("decoding armored public key file: %w", err)
return nil, fmt.Errorf("decoding armored public key file: %w", err)
}
} else {
keyring, err = openpgp.ReadKeyRing(bytes.NewReader(keyFileContent))
if err != nil {
return fmt.Errorf("decoding public key file: %w", err)
return nil, fmt.Errorf("decoding public key file: %w", err)
}
}

block, _ := clearsign.Decode(message)
_, err = block.VerifySignature(keyring, nil)

return err
return block.Plaintext, err
}

func parseKeyID(hexKeyID *string) (uint64, error) {
Expand Down

0 comments on commit b3e7ff9

Please sign in to comment.