diff options
| author | 2026-04-02 06:23:30 +0000 | |
|---|---|---|
| committer | 2026-04-02 06:28:39 +0000 | |
| commit | a041d523de389b65b98a5373a8034041db2a8d83 (patch) | |
| tree | 7b423dc735f463be616045f2c3c2095a7737aca7 /object/signed | |
| parent | research: Add dynamic pack resources (diff) | |
| signature | No signature | |
*: Remove
Diffstat (limited to 'object/signed')
| -rw-r--r-- | object/signed/commit/commit.go | 15 | ||||
| -rw-r--r-- | object/signed/commit/doc.go | 6 | ||||
| -rw-r--r-- | object/signed/commit/integration_test.go | 138 | ||||
| -rw-r--r-- | object/signed/commit/parse.go | 107 | ||||
| -rw-r--r-- | object/signed/commit/payload_append.go | 11 | ||||
| -rw-r--r-- | object/signed/commit/signature_algorithms.go | 16 | ||||
| -rw-r--r-- | object/signed/commit/signature_append.go | 17 | ||||
| -rw-r--r-- | object/signed/commit/unit_test.go | 170 | ||||
| -rw-r--r-- | object/signed/doc.go | 7 | ||||
| -rw-r--r-- | object/signed/tag/doc.go | 3 | ||||
| -rw-r--r-- | object/signed/tag/integration_test.go | 139 | ||||
| -rw-r--r-- | object/signed/tag/parse.go | 143 | ||||
| -rw-r--r-- | object/signed/tag/payload_append.go | 11 | ||||
| -rw-r--r-- | object/signed/tag/signature_algorithms.go | 16 | ||||
| -rw-r--r-- | object/signed/tag/signature_append.go | 17 | ||||
| -rw-r--r-- | object/signed/tag/tag.go | 15 | ||||
| -rw-r--r-- | object/signed/tag/unit_test.go | 257 |
17 files changed, 0 insertions, 1088 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) - } -} diff --git a/object/signed/doc.go b/object/signed/doc.go deleted file mode 100644 index fb6fc3f8..00000000 --- a/object/signed/doc.go +++ /dev/null @@ -1,7 +0,0 @@ -// Package signed encapsulates raw signed-object processing. -// -// Its subpackages extract verification payloads and embedded signatures from -// raw commit and tag object bodies, without depending on the parsed -// object models in [codeberg.org/lindenii/furgit/object/commit] and -// [codeberg.org/lindenii/furgit/object/tag]. -package signed diff --git a/object/signed/tag/doc.go b/object/signed/tag/doc.go deleted file mode 100644 index 22b1098a..00000000 --- a/object/signed/tag/doc.go +++ /dev/null @@ -1,3 +0,0 @@ -// Package signedtag extracts tag signing payloads and signatures from raw tag -// object bodies. -package signedtag diff --git a/object/signed/tag/integration_test.go b/object/signed/tag/integration_test.go deleted file mode 100644 index af32aa02..00000000 --- a/object/signed/tag/integration_test.go +++ /dev/null @@ -1,139 +0,0 @@ -package signedtag_test - -import ( - "bytes" - "os" - "os/exec" - "path/filepath" - "testing" - - "codeberg.org/lindenii/furgit/internal/testgit" - objectid "codeberg.org/lindenii/furgit/object/id" - signedtag "codeberg.org/lindenii/furgit/object/signed/tag" -) - -func setupSSHSignedTag( - 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, "tag.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", "-m", "base commit") - testRepo.Run(t, "tag", "-s", "-m", "signed tag", "signed-tag") - - tagID := testRepo.RevParse(t, "signed-tag^{tag}") - body := testRepo.CatFile(t, "tag", tagID) - - tag, err := signedtag.Parse(body, algo) - if err != nil { - t.Fatalf("Parse: %v", err) - } - - signature, ok := tag.AppendSignature(nil, algo) - if !ok { - t.Fatal("missing signature") - } - - err = signRoot.WriteFile("tag.sig", signature, 0o600) - if err != nil { - t.Fatalf("WriteFile(tag.sig): %v", err) - } - - return tag.AppendPayload(nil), allowedSignersPath, signaturePath -} - -func TestSSHSignedTagIntegration(t *testing.T) { - t.Parallel() - - testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) { //nolint:thelper - payload, allowedSignersPath, signaturePath := setupSSHSignedTag(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 TestSSHSignedTagIntegrationRejectsTamperedPayload(t *testing.T) { - t.Parallel() - - testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) { //nolint:thelper - payload, allowedSignersPath, signaturePath := setupSSHSignedTag(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/tag/parse.go b/object/signed/tag/parse.go deleted file mode 100644 index b2061d3f..00000000 --- a/object/signed/tag/parse.go +++ /dev/null @@ -1,143 +0,0 @@ -package signedtag - -import ( - "bytes" - "slices" - - objectid "codeberg.org/lindenii/furgit/object/id" -) - -var signatureBeginLines = [][]byte{ //nolint:gochecknoglobals - []byte("-----BEGIN PGP SIGNATURE-----"), - []byte("-----BEGIN PGP MESSAGE-----"), - []byte("-----BEGIN SSH SIGNATURE-----"), - []byte("-----BEGIN SIGNED MESSAGE-----"), -} - -// Parse parses one raw tag object body for signature extraction. -// -// Git stores the signature for storageAlgo as an in-body ASCII-armored -// trailer, and may store additional signatures for other algorithms in -// gpgsig* headers. -// -// The returned Tag remains valid only while body remains unchanged. -// -// Labels: Deps-Borrowed, Life-Parent. -func Parse(body []byte, storageAlgo objectid.Algorithm) (*Tag, error) { - tag := &Tag{ - body: body, - signatures: make(map[objectid.Algorithm][]byteRange), - } - - signatureStart := len(body) - for i := 0; 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] - if slices.ContainsFunc(signatureBeginLines, func(begin []byte) bool { - return bytes.HasPrefix(line, begin) - }) { - signatureStart = lineStart - } - - i = next - } - - payloadStart := 0 - - payloadEnd := signatureStart - if signatureStart == len(body) { - payloadEnd = len(body) - } - - for i := 0; i < payloadEnd; { - lineStart := i - rel := bytes.IndexByte(body[i:payloadEnd], '\n') - next := payloadEnd - - lineEnd := payloadEnd - if rel >= 0 { - lineEnd = i + rel - next = lineEnd + 1 - } - - line := body[lineStart:lineEnd] - i = next - - if len(line) == 0 { - break - } - - if line[0] == ' ' { - continue - } - - key, valueStart, found := bytes.Cut(line, []byte{' '}) - if !found { - continue - } - - algo, ok := objectid.ParseSignatureHeaderName(string(key)) - if !ok { - continue - } - - tag.appendPayloadRange(payloadStart, lineStart) - tag.signatures[algo] = append(tag.signatures[algo], byteRange{ - start: lineEnd - len(valueStart), - end: next, - }) - - for i < payloadEnd { - rel := bytes.IndexByte(body[i:payloadEnd], '\n') - next = payloadEnd - - lineEnd = payloadEnd - if rel >= 0 { - lineEnd = i + rel - next = lineEnd + 1 - } - - cont := body[i:lineEnd] - if len(cont) == 0 || cont[0] != ' ' { - break - } - - tag.signatures[algo] = append(tag.signatures[algo], byteRange{ - start: i + 1, - end: next, - }) - - i = next - } - - payloadStart = i - } - - tag.appendPayloadRange(payloadStart, payloadEnd) - - if signatureStart != len(body) { - tag.signatures[storageAlgo] = append(tag.signatures[storageAlgo], byteRange{ - start: signatureStart, - end: len(body), - }) - } - - return tag, nil -} - -func (tag *Tag) appendPayloadRange(start, end int) { - if start >= end { - return - } - - tag.payload = append(tag.payload, byteRange{start: start, end: end}) -} diff --git a/object/signed/tag/payload_append.go b/object/signed/tag/payload_append.go deleted file mode 100644 index dae29dd8..00000000 --- a/object/signed/tag/payload_append.go +++ /dev/null @@ -1,11 +0,0 @@ -package signedtag - -// AppendPayload appends the tag verification payload to dst, omitting all -// embedded signatures. -func (tag *Tag) AppendPayload(dst []byte) []byte { - for _, part := range tag.payload { - dst = append(dst, tag.body[part.start:part.end]...) - } - - return dst -} diff --git a/object/signed/tag/signature_algorithms.go b/object/signed/tag/signature_algorithms.go deleted file mode 100644 index bc178bce..00000000 --- a/object/signed/tag/signature_algorithms.go +++ /dev/null @@ -1,16 +0,0 @@ -package signedtag - -import objectid "codeberg.org/lindenii/furgit/object/id" - -// Algorithms returns the algorithms for which the tag carries signatures. -func (tag *Tag) Algorithms() []objectid.Algorithm { - var algorithms []objectid.Algorithm - - for _, algo := range objectid.SupportedAlgorithms() { - if _, ok := tag.signatures[algo]; ok { - algorithms = append(algorithms, algo) - } - } - - return algorithms -} diff --git a/object/signed/tag/signature_append.go b/object/signed/tag/signature_append.go deleted file mode 100644 index 101816eb..00000000 --- a/object/signed/tag/signature_append.go +++ /dev/null @@ -1,17 +0,0 @@ -package signedtag - -import objectid "codeberg.org/lindenii/furgit/object/id" - -// AppendSignature appends the signature for algo to dst. -func (tag *Tag) AppendSignature(dst []byte, algo objectid.Algorithm) ([]byte, bool) { - signature, ok := tag.signatures[algo] - if !ok { - return dst, false - } - - for _, part := range signature { - dst = append(dst, tag.body[part.start:part.end]...) - } - - return dst, true -} diff --git a/object/signed/tag/tag.go b/object/signed/tag/tag.go deleted file mode 100644 index 2ebf9369..00000000 --- a/object/signed/tag/tag.go +++ /dev/null @@ -1,15 +0,0 @@ -package signedtag - -import objectid "codeberg.org/lindenii/furgit/object/id" - -// Tag represents the payload and signatures parsed from a raw tag object. -type Tag struct { - body []byte - payload []byteRange - signatures map[objectid.Algorithm][]byteRange -} - -type byteRange struct { - start int - end int -} diff --git a/object/signed/tag/unit_test.go b/object/signed/tag/unit_test.go deleted file mode 100644 index dd4ae66f..00000000 --- a/object/signed/tag/unit_test.go +++ /dev/null @@ -1,257 +0,0 @@ -package signedtag_test - -import ( - "testing" - - objectid "codeberg.org/lindenii/furgit/object/id" - signedtag "codeberg.org/lindenii/furgit/object/signed/tag" -) - -func TestParseSignedTag(t *testing.T) { - t.Parallel() - - body := []byte("" + - "object 04b871796dc0420f8e7561a895b52484b701d51a\n" + - "type commit\n" + - "tag signedtag\n" + - "tagger C O Mitter <committer@example.com> 1465981006 +0000\n" + - "gpgsig-sha256 -----BEGIN PGP SIGNATURE-----\n" + - " Version: GnuPG v1\n" + - " \n" + - " header-signature\n" + - " -----END PGP SIGNATURE-----\n" + - "\n" + - "signed tag\n" + - "\n" + - "signed tag message body\n" + - "-----BEGIN PGP SIGNATURE-----\n" + - "Version: GnuPG v1\n" + - "\n" + - "body-signature\n" + - "-----END PGP SIGNATURE-----\n") - - tag, err := signedtag.Parse(body, objectid.AlgorithmSHA1) - if err != nil { - t.Fatalf("Parse: %v", err) - } - - gotPayload := string(tag.AppendPayload(nil)) - - wantPayload := "" + - "object 04b871796dc0420f8e7561a895b52484b701d51a\n" + - "type commit\n" + - "tag signedtag\n" + - "tagger C O Mitter <committer@example.com> 1465981006 +0000\n" + - "\n" + - "signed tag\n" + - "\n" + - "signed tag message body\n" - if gotPayload != wantPayload { - t.Fatalf("payload mismatch:\n got: %q\nwant: %q", gotPayload, wantPayload) - } - - gotAlgorithms := tag.Algorithms() - if len(gotAlgorithms) != 2 || gotAlgorithms[0] != objectid.AlgorithmSHA1 || gotAlgorithms[1] != objectid.AlgorithmSHA256 { - t.Fatalf("algorithms mismatch: got %v", gotAlgorithms) - } - - gotSignature, ok := tag.AppendSignature(nil, objectid.AlgorithmSHA1) - if !ok { - t.Fatal("missing sha1 signature") - } - - wantSignature := "" + - "-----BEGIN PGP SIGNATURE-----\n" + - "Version: GnuPG v1\n" + - "\n" + - "body-signature\n" + - "-----END PGP SIGNATURE-----\n" - if string(gotSignature) != wantSignature { - t.Fatalf("signature mismatch:\n got: %q\nwant: %q", string(gotSignature), wantSignature) - } - - gotHeaderSignature, ok := tag.AppendSignature(nil, objectid.AlgorithmSHA256) - if !ok { - t.Fatal("missing sha256 signature") - } - - wantHeaderSignature := "" + - "-----BEGIN PGP SIGNATURE-----\n" + - "Version: GnuPG v1\n" + - "\n" + - "header-signature\n" + - "-----END PGP SIGNATURE-----\n" - if string(gotHeaderSignature) != wantHeaderSignature { - t.Fatalf("header signature mismatch:\n got: %q\nwant: %q", string(gotHeaderSignature), wantHeaderSignature) - } -} - -func TestParseHeaderOnlyTagStripsHeaderAndKeepsHeaderSignature(t *testing.T) { - t.Parallel() - - body := []byte("" + - "object deadbeef\n" + - "type commit\n" + - "tag signedtag\n" + - "tagger T A Gger <tagger@example.com> 1465981006 +0000\n" + - "gpgsig-sha256 header\n" + - " continued\n" + - "\n" + - "message\n") - - tag, err := signedtag.Parse(body, objectid.AlgorithmSHA1) - if err != nil { - t.Fatalf("Parse: %v", err) - } - - gotPayload := string(tag.AppendPayload(nil)) - - wantPayload := "" + - "object deadbeef\n" + - "type commit\n" + - "tag signedtag\n" + - "tagger T A Gger <tagger@example.com> 1465981006 +0000\n" + - "\n" + - "message\n" - if gotPayload != wantPayload { - t.Fatalf("payload mismatch:\n got: %q\nwant: %q", gotPayload, wantPayload) - } - - gotSignature, ok := tag.AppendSignature(nil, objectid.AlgorithmSHA256) - if !ok { - t.Fatal("missing sha256 signature") - } - - wantSignature := "" + - "header\n" + - "continued\n" - if string(gotSignature) != wantSignature { - t.Fatalf("signature mismatch:\n got: %q\nwant: %q", string(gotSignature), wantSignature) - } - - if _, ok := tag.AppendSignature(nil, objectid.AlgorithmSHA1); ok { - t.Fatal("unexpected sha1 signature") - } -} - -func TestParseKeepsUnknownHeaderSignatureTextInPayload(t *testing.T) { - t.Parallel() - - body := []byte("" + - "object deadbeef\n" + - "type commit\n" + - "tag signedtag\n" + - "tagger T A Gger <tagger@example.com> 1465981006 +0000\n" + - "gpgsig-future header\n" + - " continued\n" + - "\n" + - "message line\n" + - "-----BEGIN PGP SIGNATURE-----\n" + - "body-signature\n" + - "-----END PGP SIGNATURE-----\n") - - tag, err := signedtag.Parse(body, objectid.AlgorithmSHA1) - if err != nil { - t.Fatalf("Parse: %v", err) - } - - gotPayload := string(tag.AppendPayload(nil)) - - wantPayload := "" + - "object deadbeef\n" + - "type commit\n" + - "tag signedtag\n" + - "tagger T A Gger <tagger@example.com> 1465981006 +0000\n" + - "gpgsig-future header\n" + - " continued\n" + - "\n" + - "message line\n" - if gotPayload != wantPayload { - t.Fatalf("payload mismatch:\n got: %q\nwant: %q", gotPayload, wantPayload) - } -} - -func TestParseKeepsMessageGpgsigTextInPayload(t *testing.T) { - t.Parallel() - - body := []byte("" + - "object deadbeef\n" + - "type commit\n" + - "tag signedtag\n" + - "tagger T A Gger <tagger@example.com> 1465981006 +0000\n" + - "\n" + - "message line\n" + - "gpgsig-future header\n" + - " continued\n" + - "-----BEGIN PGP SIGNATURE-----\n" + - "body-signature\n" + - "-----END PGP SIGNATURE-----\n") - - tag, err := signedtag.Parse(body, objectid.AlgorithmSHA1) - if err != nil { - t.Fatalf("Parse: %v", err) - } - - gotPayload := string(tag.AppendPayload(nil)) - - wantPayload := "" + - "object deadbeef\n" + - "type commit\n" + - "tag signedtag\n" + - "tagger T A Gger <tagger@example.com> 1465981006 +0000\n" + - "\n" + - "message line\n" + - "gpgsig-future header\n" + - " continued\n" - if gotPayload != wantPayload { - t.Fatalf("payload mismatch:\n got: %q\nwant: %q", gotPayload, wantPayload) - } -} - -func TestParseUsesLastSignatureBeginByPrefix(t *testing.T) { - t.Parallel() - - body := []byte("" + - "object deadbeef\n" + - "type commit\n" + - "tag signedtag\n" + - "tagger T A Gger <tagger@example.com> 1465981006 +0000\n" + - "\n" + - "message\n" + - "-----BEGIN PGP SIGNATURE----- stray\n" + - "still message\n" + - "-----BEGIN PGP SIGNATURE----- trailing\n" + - "body-signature\n") - - tag, err := signedtag.Parse(body, objectid.AlgorithmSHA1) - if err != nil { - t.Fatalf("Parse: %v", err) - } - - gotPayload := string(tag.AppendPayload(nil)) - - wantPayload := "" + - "object deadbeef\n" + - "type commit\n" + - "tag signedtag\n" + - "tagger T A Gger <tagger@example.com> 1465981006 +0000\n" + - "\n" + - "message\n" + - "-----BEGIN PGP SIGNATURE----- stray\n" + - "still message\n" - if gotPayload != wantPayload { - t.Fatalf("payload mismatch:\n got: %q\nwant: %q", gotPayload, wantPayload) - } - - gotSignature, ok := tag.AppendSignature(nil, objectid.AlgorithmSHA1) - if !ok { - t.Fatal("missing signature") - } - - wantSignature := "" + - "-----BEGIN PGP SIGNATURE----- trailing\n" + - "body-signature\n" - if string(gotSignature) != wantSignature { - t.Fatalf("signature mismatch:\n got: %q\nwant: %q", string(gotSignature), wantSignature) - } -} |
