aboutsummaryrefslogtreecommitdiff
path: root/object/signed/commit
diff options
context:
space:
mode:
Diffstat (limited to 'object/signed/commit')
-rw-r--r--object/signed/commit/commit.go15
-rw-r--r--object/signed/commit/doc.go6
-rw-r--r--object/signed/commit/integration_test.go138
-rw-r--r--object/signed/commit/parse.go107
-rw-r--r--object/signed/commit/payload_append.go11
-rw-r--r--object/signed/commit/signature_algorithms.go16
-rw-r--r--object/signed/commit/signature_append.go17
-rw-r--r--object/signed/commit/unit_test.go170
8 files changed, 0 insertions, 480 deletions
diff --git a/object/signed/commit/commit.go b/object/signed/commit/commit.go
deleted file mode 100644
index cd0ff197..00000000
--- a/object/signed/commit/commit.go
+++ /dev/null
@@ -1,15 +0,0 @@
-package signedcommit
-
-import objectid "codeberg.org/lindenii/furgit/object/id"
-
-// Commit represents the payload and signatures parsed from a raw comit object.
-type Commit struct {
- body []byte
- payload []byteRange
- signatures map[objectid.Algorithm][]byteRange
-}
-
-type byteRange struct {
- start int
- end int
-}
diff --git a/object/signed/commit/doc.go b/object/signed/commit/doc.go
deleted file mode 100644
index 91da6fa8..00000000
--- a/object/signed/commit/doc.go
+++ /dev/null
@@ -1,6 +0,0 @@
-// Package signedcommit extracts commit signing payloads and signatures from raw
-// commit object bodies.
-package signedcommit
-
-// TODO: Consider whether we want to fully copy the bytes into here.
-// The Append functions are a bit weird ergonomically.
diff --git a/object/signed/commit/integration_test.go b/object/signed/commit/integration_test.go
deleted file mode 100644
index 82b34b14..00000000
--- a/object/signed/commit/integration_test.go
+++ /dev/null
@@ -1,138 +0,0 @@
-package signedcommit_test
-
-import (
- "bytes"
- "os"
- "os/exec"
- "path/filepath"
- "testing"
-
- "codeberg.org/lindenii/furgit/internal/testgit"
- objectid "codeberg.org/lindenii/furgit/object/id"
- signedcommit "codeberg.org/lindenii/furgit/object/signed/commit"
-)
-
-func setupSSHSignedCommit(
- t *testing.T,
- algo objectid.Algorithm,
-) (payload []byte, allowedSignersPath string, signaturePath string) {
- t.Helper()
-
- testRepo := testgit.NewRepo(t, testgit.RepoOptions{ObjectFormat: algo})
-
- signDir := t.TempDir()
-
- signRoot, err := os.OpenRoot(signDir)
- if err != nil {
- t.Fatalf("os.OpenRoot(%q): %v", signDir, err)
- }
-
- t.Cleanup(func() { _ = signRoot.Close() })
-
- privateKeyPath := filepath.Join(signDir, "signing_key")
- allowedSignersPath = filepath.Join(signDir, "allowed_signers")
- signaturePath = filepath.Join(signDir, "commit.sig")
-
- cmd := exec.Command( //nolint:noctx
- "ssh-keygen",
- "-q",
- "-t", "ed25519",
- "-N", "",
- "-C", "runxiyu@umich.edu",
- "-f", privateKeyPath,
- ) //#nosec G204
-
- out, err := cmd.CombinedOutput()
- if err != nil {
- t.Fatalf("ssh-keygen generate failed: %v\n%s", err, out)
- }
-
- publicKey, err := signRoot.ReadFile("signing_key.pub")
- if err != nil {
- t.Fatalf("ReadFile(signing_key.pub): %v", err)
- }
-
- err = signRoot.WriteFile(
- "allowed_signers",
- append([]byte("runxiyu@umich.edu "), publicKey...),
- 0o600,
- )
- if err != nil {
- t.Fatalf("WriteFile(allowed_signers): %v", err)
- }
-
- testRepo.Run(t, "config", "gpg.format", "ssh")
- testRepo.Run(t, "config", "user.signingkey", privateKeyPath)
-
- testRepo.WriteFile(t, "file.txt", []byte("signed\n"), 0o644)
- testRepo.Run(t, "add", "file.txt")
- testRepo.Run(t, "commit", "-S", "-m", "signed commit")
-
- commitID := testRepo.RevParse(t, "HEAD^{commit}")
- body := testRepo.CatFile(t, "commit", commitID)
-
- commit, err := signedcommit.Parse(body)
- if err != nil {
- t.Fatalf("Parse: %v", err)
- }
-
- signature, ok := commit.AppendSignature(nil, algo)
- if !ok {
- t.Fatalf("missing %s signature", algo)
- }
-
- err = signRoot.WriteFile("commit.sig", signature, 0o600)
- if err != nil {
- t.Fatalf("WriteFile(commit.sig): %v", err)
- }
-
- return commit.AppendPayload(nil), allowedSignersPath, signaturePath
-}
-
-func TestSSHSignedCommitIntegration(t *testing.T) {
- t.Parallel()
-
- testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) { //nolint:thelper
- payload, allowedSignersPath, signaturePath := setupSSHSignedCommit(t, algo)
-
- cmd := exec.Command( //nolint:noctx
- "ssh-keygen",
- "-Y", "verify",
- "-n", "git",
- "-f", allowedSignersPath,
- "-I", "runxiyu@umich.edu",
- "-s", signaturePath,
- ) //#nosec G204
- cmd.Stdin = bytes.NewReader(payload)
-
- out, err := cmd.CombinedOutput()
- if err != nil {
- t.Fatalf("ssh-keygen verify failed: %v\n%s", err, out)
- }
- })
-}
-
-func TestSSHSignedCommitIntegrationRejectsTamperedPayload(t *testing.T) {
- t.Parallel()
-
- testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) { //nolint:thelper
- payload, allowedSignersPath, signaturePath := setupSSHSignedCommit(t, algo)
- payload = append([]byte(nil), payload...)
- payload[len(payload)-2] ^= 1
-
- cmd := exec.Command( //nolint:noctx
- "ssh-keygen",
- "-Y", "verify",
- "-n", "git",
- "-f", allowedSignersPath,
- "-I", "runxiyu@umich.edu",
- "-s", signaturePath,
- ) //#nosec G204
- cmd.Stdin = bytes.NewReader(payload)
-
- out, err := cmd.CombinedOutput()
- if err == nil {
- t.Fatalf("ssh-keygen verify unexpectedly succeeded:\n%s", out)
- }
- })
-}
diff --git a/object/signed/commit/parse.go b/object/signed/commit/parse.go
deleted file mode 100644
index fa498093..00000000
--- a/object/signed/commit/parse.go
+++ /dev/null
@@ -1,107 +0,0 @@
-package signedcommit
-
-import (
- "bytes"
-
- objectid "codeberg.org/lindenii/furgit/object/id"
-)
-
-// Parse parses one raw commit object body for signature extraction.
-//
-// The returned Commit remains valid only while body remains unchanged.
-//
-// Labels: Deps-Borrowed, Life-Parent.
-func Parse(body []byte) (*Commit, error) {
- commit := &Commit{
- body: body,
- signatures: make(map[objectid.Algorithm][]byteRange),
- }
-
- payloadStart := 0
- i := 0
-
- for i < len(body) {
- lineStart := i
-
- rel := bytes.IndexByte(body[i:], '\n')
- next := len(body)
-
- lineEnd := len(body)
- if rel >= 0 {
- lineEnd = i + rel
- next = lineEnd + 1
- }
-
- line := body[lineStart:lineEnd]
- i = next
-
- if len(line) == 0 {
- commit.appendPayloadRange(payloadStart, len(body))
-
- return commit, nil
- }
-
- if line[0] == ' ' {
- continue
- }
-
- if !bytes.HasPrefix(line, []byte("gpgsig")) {
- continue
- }
-
- commit.appendPayloadRange(payloadStart, lineStart)
-
- key, valueStart, found := bytes.Cut(line, []byte{' '})
- if found {
- if algo, ok := objectid.ParseSignatureHeaderName(string(key)); ok {
- commit.signatures[algo] = append(commit.signatures[algo], byteRange{
- start: lineEnd - len(valueStart),
- end: next,
- })
- }
- }
-
- for i < len(body) {
- rel := bytes.IndexByte(body[i:], '\n')
- next = len(body)
-
- lineEnd = len(body)
- if rel >= 0 {
- lineEnd = i + rel
- next = lineEnd + 1
- }
-
- contStart := i
-
- cont := body[contStart:lineEnd]
- if len(cont) == 0 || cont[0] != ' ' {
- break
- }
-
- if found {
- if algo, ok := objectid.ParseSignatureHeaderName(string(key)); ok {
- commit.signatures[algo] = append(commit.signatures[algo], byteRange{
- start: contStart + 1,
- end: next,
- })
- }
- }
-
- i = next
- }
-
- payloadStart = i
- }
-
- commit.appendPayloadRange(payloadStart, len(body))
-
- return commit, nil
-}
-
-func (commit *Commit) appendPayloadRange(start, end int) {
- if start >= end {
- return
- }
-
- commit.payload = append(commit.payload, byteRange{start: start, end: end})
-}
diff --git a/object/signed/commit/payload_append.go b/object/signed/commit/payload_append.go
deleted file mode 100644
index c261910a..00000000
--- a/object/signed/commit/payload_append.go
+++ /dev/null
@@ -1,11 +0,0 @@
-package signedcommit
-
-// AppendPayload appends the commit verification payload to dst, omitting all
-// embedded signature headers.
-func (commit *Commit) AppendPayload(dst []byte) []byte {
- for _, part := range commit.payload {
- dst = append(dst, commit.body[part.start:part.end]...)
- }
-
- return dst
-}
diff --git a/object/signed/commit/signature_algorithms.go b/object/signed/commit/signature_algorithms.go
deleted file mode 100644
index ac763706..00000000
--- a/object/signed/commit/signature_algorithms.go
+++ /dev/null
@@ -1,16 +0,0 @@
-package signedcommit
-
-import objectid "codeberg.org/lindenii/furgit/object/id"
-
-// Algorithms returns the algorithms for which the commit carries signatures.
-func (commit *Commit) Algorithms() []objectid.Algorithm {
- var algorithms []objectid.Algorithm
-
- for _, algo := range objectid.SupportedAlgorithms() {
- if _, ok := commit.signatures[algo]; ok {
- algorithms = append(algorithms, algo)
- }
- }
-
- return algorithms
-}
diff --git a/object/signed/commit/signature_append.go b/object/signed/commit/signature_append.go
deleted file mode 100644
index 7f9144b7..00000000
--- a/object/signed/commit/signature_append.go
+++ /dev/null
@@ -1,17 +0,0 @@
-package signedcommit
-
-import objectid "codeberg.org/lindenii/furgit/object/id"
-
-// AppendSignature appends the unfolded signature for algo to dst.
-func (commit *Commit) AppendSignature(dst []byte, algo objectid.Algorithm) ([]byte, bool) {
- signature, ok := commit.signatures[algo]
- if !ok {
- return dst, false
- }
-
- for _, part := range signature {
- dst = append(dst, commit.body[part.start:part.end]...)
- }
-
- return dst, true
-}
diff --git a/object/signed/commit/unit_test.go b/object/signed/commit/unit_test.go
deleted file mode 100644
index 88d4fa3b..00000000
--- a/object/signed/commit/unit_test.go
+++ /dev/null
@@ -1,170 +0,0 @@
-package signedcommit_test
-
-import (
- "slices"
- "testing"
-
- objectid "codeberg.org/lindenii/furgit/object/id"
- signedcommit "codeberg.org/lindenii/furgit/object/signed/commit"
-)
-
-func TestParseUpstreamMultiplySignedCommit(t *testing.T) {
- t.Parallel()
-
- // t/t7510-signed-commit.sh
- body := []byte("" +
- "tree 0cfbf08886fca9a91cb753ec8734c84fcbe52c9f\n" +
- "parent 9da738312d24ef0a29be2c8c2b6fc5cf7085a293\n" +
- "author A U Thor <author@example.com> 1112912653 -0700\n" +
- "committer C O Mitter <committer@example.com> 1112912653 -0700\n" +
- "gpgsig -----BEGIN PGP SIGNATURE-----\n" +
- " \n" +
- " iHQEABECADQWIQRz11h0S+chaY7FTocTtvUezd5DDQUCX/uBDRYcY29tbWl0dGVy\n" +
- " QGV4YW1wbGUuY29tAAoJEBO29R7N3kMNd+8AoK1I8mhLHviPH+q2I5fIVgPsEtYC\n" +
- " AKCTqBh+VabJceXcGIZuF0Ry+udbBQ==\n" +
- " =tQ0N\n" +
- " -----END PGP SIGNATURE-----\n" +
- "gpgsig-sha256 -----BEGIN PGP SIGNATURE-----\n" +
- " \n" +
- " iHQEABECADQWIQRz11h0S+chaY7FTocTtvUezd5DDQUCX/uBIBYcY29tbWl0dGVy\n" +
- " QGV4YW1wbGUuY29tAAoJEBO29R7N3kMN/NEAn0XO9RYSBj2dFyozi0JKSbssYMtO\n" +
- " AJwKCQ1BQOtuwz//IjU8TiS+6S4iUw==\n" +
- " =pIwP\n" +
- " -----END PGP SIGNATURE-----\n" +
- "\n" +
- "second\n")
-
- commit, err := signedcommit.Parse(body)
- if err != nil {
- t.Fatalf("Parse: %v", err)
- }
-
- gotPayload := string(commit.AppendPayload(nil))
-
- wantPayload := "" +
- "tree 0cfbf08886fca9a91cb753ec8734c84fcbe52c9f\n" +
- "parent 9da738312d24ef0a29be2c8c2b6fc5cf7085a293\n" +
- "author A U Thor <author@example.com> 1112912653 -0700\n" +
- "committer C O Mitter <committer@example.com> 1112912653 -0700\n" +
- "\n" +
- "second\n"
- if gotPayload != wantPayload {
- t.Fatalf("payload mismatch:\n got: %q\nwant: %q", gotPayload, wantPayload)
- }
-
- gotSHA1, ok := commit.AppendSignature(nil, objectid.AlgorithmSHA1)
- if !ok {
- t.Fatal("missing sha1 signature")
- }
-
- wantSHA1 := "" +
- "-----BEGIN PGP SIGNATURE-----\n" +
- "\n" +
- "iHQEABECADQWIQRz11h0S+chaY7FTocTtvUezd5DDQUCX/uBDRYcY29tbWl0dGVy\n" +
- "QGV4YW1wbGUuY29tAAoJEBO29R7N3kMNd+8AoK1I8mhLHviPH+q2I5fIVgPsEtYC\n" +
- "AKCTqBh+VabJceXcGIZuF0Ry+udbBQ==\n" +
- "=tQ0N\n" +
- "-----END PGP SIGNATURE-----\n"
- if string(gotSHA1) != wantSHA1 {
- t.Fatalf("sha1 signature mismatch:\n got: %q\nwant: %q", string(gotSHA1), wantSHA1)
- }
-
- gotSHA256, ok := commit.AppendSignature(nil, objectid.AlgorithmSHA256)
- if !ok {
- t.Fatal("missing sha256 signature")
- }
-
- wantSHA256 := "" +
- "-----BEGIN PGP SIGNATURE-----\n" +
- "\n" +
- "iHQEABECADQWIQRz11h0S+chaY7FTocTtvUezd5DDQUCX/uBIBYcY29tbWl0dGVy\n" +
- "QGV4YW1wbGUuY29tAAoJEBO29R7N3kMN/NEAn0XO9RYSBj2dFyozi0JKSbssYMtO\n" +
- "AJwKCQ1BQOtuwz//IjU8TiS+6S4iUw==\n" +
- "=pIwP\n" +
- "-----END PGP SIGNATURE-----\n"
- if string(gotSHA256) != wantSHA256 {
- t.Fatalf("sha256 signature mismatch:\n got: %q\nwant: %q", string(gotSHA256), wantSHA256)
- }
-
- gotAlgorithms := commit.Algorithms()
-
- wantAlgorithms := []objectid.Algorithm{
- objectid.AlgorithmSHA1,
- objectid.AlgorithmSHA256,
- }
- if !slices.Equal(gotAlgorithms, wantAlgorithms) {
- t.Fatalf("Algorithms() = %v, want %v", gotAlgorithms, wantAlgorithms)
- }
-}
-
-func TestParseStripsUnknownGpgsigHeadersFromPayload(t *testing.T) {
- t.Parallel()
-
- body := []byte("" +
- "tree deadbeef\n" +
- "gpgsig-future header\n" +
- " continued\n" +
- "\n" +
- "message\n")
-
- commit, err := signedcommit.Parse(body)
- if err != nil {
- t.Fatalf("Parse: %v", err)
- }
-
- gotPayload := string(commit.AppendPayload(nil))
-
- wantPayload := "" +
- "tree deadbeef\n" +
- "\n" +
- "message\n"
- if gotPayload != wantPayload {
- t.Fatalf("payload mismatch:\n got: %q\nwant: %q", gotPayload, wantPayload)
- }
-
- if gotAlgorithms := commit.Algorithms(); len(gotAlgorithms) != 0 {
- t.Fatalf("Algorithms() = %v, want none", gotAlgorithms)
- }
-}
-
-func TestParseAllowsDuplicateSignatureHeaders(t *testing.T) {
- t.Parallel()
-
- body := []byte("" +
- "tree deadbeef\n" +
- "gpgsig one\n" +
- " two\n" +
- "gpgsig three\n" +
- " four\n" +
- "\n" +
- "message\n")
-
- commit, err := signedcommit.Parse(body)
- if err != nil {
- t.Fatalf("Parse: %v", err)
- }
-
- gotPayload := string(commit.AppendPayload(nil))
-
- wantPayload := "" +
- "tree deadbeef\n" +
- "\n" +
- "message\n"
- if gotPayload != wantPayload {
- t.Fatalf("payload mismatch:\n got: %q\nwant: %q", gotPayload, wantPayload)
- }
-
- gotSignature, ok := commit.AppendSignature(nil, objectid.AlgorithmSHA1)
- if !ok {
- t.Fatal("missing sha1 signature")
- }
-
- wantSignature := "" +
- "one\n" +
- "two\n" +
- "three\n" +
- "four\n"
- if string(gotSignature) != wantSignature {
- t.Fatalf("signature mismatch:\n got: %q\nwant: %q", string(gotSignature), wantSignature)
- }
-}