Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitaly.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIgor Drozdov <idrozdov@gitlab.com>2023-04-11 12:04:42 +0300
committerIgor Drozdov <idrozdov@gitlab.com>2023-06-07 17:58:51 +0300
commit2a1007bee30206a282312d73186ff82c379f6aa9 (patch)
tree6193d61dacb408180a4b2f39cd3286ae4720a3f6 /internal/git2go
parent7c07fed2a6dc6f6db529d65743eb5ed81b42833f (diff)
Use SSH for signing commits
This commit uses x/crypto library instead of its fork in order to implement signing via SSH Changelog: changed
Diffstat (limited to 'internal/git2go')
-rw-r--r--internal/git2go/commit_test.go175
-rw-r--r--internal/git2go/testdata/signing_ssh_key12
-rw-r--r--internal/git2go/testdata/signing_ssh_key_ecdsa12
-rw-r--r--internal/git2go/testdata/signing_ssh_key_ecdsa.pub1
-rw-r--r--internal/git2go/testdata/signing_ssh_key_ed255198
-rw-r--r--internal/git2go/testdata/signing_ssh_key_ed25519.pub1
-rw-r--r--internal/git2go/testdata/signing_ssh_key_ed25519.sig6
-rw-r--r--internal/git2go/testdata/signing_ssh_key_rsa50
-rw-r--r--internal/git2go/testdata/signing_ssh_key_rsa.pub1
-rw-r--r--internal/git2go/testdata/signing_ssh_key_rsa.sig26
10 files changed, 249 insertions, 43 deletions
diff --git a/internal/git2go/commit_test.go b/internal/git2go/commit_test.go
index a34c64d8b..9dd7182c3 100644
--- a/internal/git2go/commit_test.go
+++ b/internal/git2go/commit_test.go
@@ -14,6 +14,7 @@ import (
"github.com/ProtonMail/go-crypto/openpgp"
"github.com/ProtonMail/go-crypto/openpgp/packet"
"github.com/stretchr/testify/require"
+ "gitlab.com/gitlab-org/gitaly/v16/cmd/gitaly-git2go/git2goutil"
"gitlab.com/gitlab-org/gitaly/v16/internal/git"
"gitlab.com/gitlab-org/gitaly/v16/internal/git/gittest"
"gitlab.com/gitlab-org/gitaly/v16/internal/git/localrepo"
@@ -32,11 +33,15 @@ func TestExecutor_Commit(t *testing.T) {
ExecutableMode = "100755"
)
+ type signingKey struct {
+ path string
+ sigPath string
+ }
type step struct {
actions []Action
error error
treeEntries []gittest.TreeEntry
- testCommit func(testing.TB, git.ObjectID)
+ testCommit func(testing.TB, git.ObjectID, signingKey)
}
ctx := testhelper.Context(t)
@@ -57,10 +62,12 @@ func TestExecutor_Commit(t *testing.T) {
executor := NewExecutor(cfg, gittest.NewCommandFactory(t, cfg), config.NewLocator(cfg))
+ author := defaultCommitAuthorSignature()
+ committer := defaultCommitAuthorSignature()
+
for _, tc := range []struct {
- desc string
- steps []step
- signingKeyPath string
+ desc string
+ steps []step
}{
{
desc: "create directory",
@@ -451,6 +458,96 @@ func TestExecutor_Commit(t *testing.T) {
},
},
},
+ } {
+ t.Run(tc.desc, func(t *testing.T) {
+ var parentCommit git.ObjectID
+ var parentIds []string
+ for i, step := range tc.steps {
+ message := fmt.Sprintf("commit %d", i+1)
+ commitID, err := executor.Commit(ctx, repo, CommitCommand{
+ Repository: repoPath,
+ Author: author,
+ Committer: committer,
+ Message: message,
+ Parent: parentCommit.String(),
+ Actions: step.actions,
+ })
+
+ if step.error != nil {
+ require.True(t, errors.Is(err, step.error), "expected: %q, actual: %q", step.error, err)
+ continue
+ } else {
+ require.NoError(t, err)
+ }
+
+ commit, err := repo.ReadCommit(ctx, commitID.Revision())
+ require.NoError(t, err)
+
+ require.Equal(t, parentIds, commit.ParentIds)
+ require.Equal(t, gittest.DefaultCommitAuthor, commit.Author)
+ require.Equal(t, gittest.DefaultCommitAuthor, commit.Committer)
+ require.Equal(t, []byte(message), commit.Body)
+
+ gittest.RequireTree(t, cfg, repoPath, commitID.String(), step.treeEntries)
+ parentCommit = commitID
+ parentIds = []string{string(commitID)}
+ }
+ })
+ }
+
+ for _, tc := range []struct {
+ desc string
+ steps []step
+ signingKeys []signingKey
+ }{
+ {
+ desc: "update created file, sign commit and verify signature using SSH signing key",
+ steps: []step{
+ {
+ actions: []Action{
+ CreateFile{Path: "file", OID: originalFile.String()},
+ UpdateFile{Path: "file", OID: updatedFile.String()},
+ },
+ treeEntries: []gittest.TreeEntry{
+ {Mode: DefaultMode, Path: "file", Content: "updated"},
+ },
+ testCommit: func(tb testing.TB, commitID git.ObjectID, key signingKey) {
+ gpgsig, dataWithoutGpgSig := extractSignature(t, ctx, repo, commitID)
+
+ err := git2goutil.VerifySSHSignature(key.path+".pub", []byte(gpgsig), []byte(dataWithoutGpgSig))
+ require.NoError(tb, err)
+
+ // Verify that the generated signature equals the one generated by Git for the identical content.
+ // Git is using ssh-keygen by default.
+ // The content to sign can be printed from dataWithoutGpgSig variable and saved to a file.
+ // Beware that a new-line is not added to that file.
+ // The content generated by ssh-keygen for RSA and ED25519 must be the same as the one in sig files.
+ // ECDSA generates a different signature every time the content is signed.
+ //
+ //go:generate ssh-keygen -Y sign -n git -f <path-to-private-key> <path-to-commit-content>
+ if key.sigPath != "" {
+ expectedSig, err := os.ReadFile(key.sigPath)
+ require.NoError(tb, err)
+ require.Equal(tb, string(expectedSig), gpgsig)
+ }
+ },
+ },
+ },
+ // Keys that are used to sign commits using SSH
+ //
+ //go:generate ssh-keygen -t <key-type>
+ signingKeys: []signingKey{
+ {
+ path: "testdata/signing_ssh_key_ed25519",
+ sigPath: "testdata/signing_ssh_key_ed25519.sig",
+ }, {
+ path: "testdata/signing_ssh_key_rsa",
+ sigPath: "testdata/signing_ssh_key_rsa.sig",
+ }, {
+ path: "testdata/signing_ssh_key_ecdsa",
+ },
+ },
+ },
{
desc: "update created file, sign commit and verify signature using GPG signing key",
steps: []step{
@@ -462,10 +559,10 @@ func TestExecutor_Commit(t *testing.T) {
treeEntries: []gittest.TreeEntry{
{Mode: DefaultMode, Path: "file", Content: "updated"},
},
- testCommit: func(tb testing.TB, commitID git.ObjectID) {
+ testCommit: func(tb testing.TB, commitID git.ObjectID, key signingKey) {
gpgsig, dataWithoutGpgSig := extractSignature(t, ctx, repo, commitID)
- file, err := os.Open("testdata/signing_gpg_key.pub")
+ file, err := os.Open(key.path + ".pub")
require.NoError(tb, err)
defer testhelper.MustClose(tb, file)
@@ -482,52 +579,44 @@ func TestExecutor_Commit(t *testing.T) {
},
},
},
- signingKeyPath: "testdata/signing_gpg_key",
+ // Keys that are used to sign commits using GPG
+ //
+ //go:generate gpg --gen-key
+ signingKeys: []signingKey{
+ {
+ path: "testdata/signing_gpg_key",
+ },
+ },
},
} {
- t.Run(tc.desc, func(t *testing.T) {
- author := defaultCommitAuthorSignature()
- committer := defaultCommitAuthorSignature()
-
- executor.signingKey = tc.signingKeyPath
+ for _, signingKey := range tc.signingKeys {
+ t.Run(tc.desc+" with "+signingKey.path, func(t *testing.T) {
+ executor.signingKey = signingKey.path
- var parentCommit git.ObjectID
- var parentIds []string
- for i, step := range tc.steps {
- message := fmt.Sprintf("commit %d", i+1)
- commitID, err := executor.Commit(ctx, repo, CommitCommand{
- Repository: repoPath,
- Author: author,
- Committer: committer,
- Message: message,
- Parent: parentCommit.String(),
- Actions: step.actions,
- })
+ for i, step := range tc.steps {
+ message := fmt.Sprintf("commit %d", i+1)
+ commitID, err := executor.Commit(ctx, repo, CommitCommand{
+ Repository: repoPath,
+ Author: author,
+ Committer: committer,
+ Message: message,
+ Actions: step.actions,
+ })
+ require.NoError(t, err)
- if step.error != nil {
- require.True(t, errors.Is(err, step.error), "expected: %q, actual: %q", step.error, err)
- continue
- } else {
+ commit, err := repo.ReadCommit(ctx, commitID.Revision())
require.NoError(t, err)
- }
- commit, err := repo.ReadCommit(ctx, commitID.Revision())
- require.NoError(t, err)
+ require.Equal(t, gittest.DefaultCommitAuthor, commit.Author)
+ require.Equal(t, gittest.DefaultCommitAuthor, commit.Committer)
+ require.Equal(t, []byte(message), commit.Body)
- require.Equal(t, parentIds, commit.ParentIds)
- require.Equal(t, gittest.DefaultCommitAuthor, commit.Author)
- require.Equal(t, gittest.DefaultCommitAuthor, commit.Committer)
- require.Equal(t, []byte(message), commit.Body)
+ step.testCommit(t, commitID, signingKey)
- if step.testCommit != nil {
- step.testCommit(t, commitID)
+ gittest.RequireTree(t, cfg, repoPath, commitID.String(), step.treeEntries)
}
-
- gittest.RequireTree(t, cfg, repoPath, commitID.String(), step.treeEntries)
- parentCommit = commitID
- parentIds = []string{string(commitID)}
- }
- })
+ })
+ }
}
}
diff --git a/internal/git2go/testdata/signing_ssh_key b/internal/git2go/testdata/signing_ssh_key
new file mode 100644
index 000000000..b1199d0ae
--- /dev/null
+++ b/internal/git2go/testdata/signing_ssh_key
@@ -0,0 +1,12 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAArAAAABNlY2RzYS
+1zaGEyLW5pc3RwNTIxAAAACG5pc3RwNTIxAAAAhQQAzQ4LqU+abLzWoRsvjA0JJgxhaVqY
+MF7cZQh+x9U7E7d2NkLTGk/O8AzXI0x37C+EXbH3SfC5zAUlKRBKsmnBU3kBMwg5ek0hqe
+kvPtl689m0m3Wx7GNLFNFbONba7LFl/nYjBZQ6VWh3hdUcyzQOs23A7wXBjMIMdDmp5N1B
+2+9D28QAAAEo6zdUCOs3VAgAAAATZWNkc2Etc2hhMi1uaXN0cDUyMQAAAAhuaXN0cDUyMQ
+AAAIUEAM0OC6lPmmy81qEbL4wNCSYMYWlamDBe3GUIfsfVOxO3djZC0xpPzvAM1yNMd+wv
+hF2x90nwucwFJSkQSrJpwVN5ATMIOXpNIanpLz7ZevPZtJt1sexjSxTRWzjW2uyxZf52Iw
+WUOlVod4XVHMs0DrNtwO8FwYzCDHQ5qeTdQdvvQ9vEAAAAQgHys1d6LlKiRjws2JrctuJj
+2BT8cnSdPmot04q3MRWQz6Y+2sk0IsWCgIY08vl4tveA4DsYajoaMtAhAvOdXr2fnAAAAC
+VpZ29yZHJvemRvdkBJZ29ycy1NYWNCb29rLVByby0yLmxvY2FsAQIDBAU=
+-----END OPENSSH PRIVATE KEY-----
diff --git a/internal/git2go/testdata/signing_ssh_key_ecdsa b/internal/git2go/testdata/signing_ssh_key_ecdsa
new file mode 100644
index 000000000..b1199d0ae
--- /dev/null
+++ b/internal/git2go/testdata/signing_ssh_key_ecdsa
@@ -0,0 +1,12 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAArAAAABNlY2RzYS
+1zaGEyLW5pc3RwNTIxAAAACG5pc3RwNTIxAAAAhQQAzQ4LqU+abLzWoRsvjA0JJgxhaVqY
+MF7cZQh+x9U7E7d2NkLTGk/O8AzXI0x37C+EXbH3SfC5zAUlKRBKsmnBU3kBMwg5ek0hqe
+kvPtl689m0m3Wx7GNLFNFbONba7LFl/nYjBZQ6VWh3hdUcyzQOs23A7wXBjMIMdDmp5N1B
+2+9D28QAAAEo6zdUCOs3VAgAAAATZWNkc2Etc2hhMi1uaXN0cDUyMQAAAAhuaXN0cDUyMQ
+AAAIUEAM0OC6lPmmy81qEbL4wNCSYMYWlamDBe3GUIfsfVOxO3djZC0xpPzvAM1yNMd+wv
+hF2x90nwucwFJSkQSrJpwVN5ATMIOXpNIanpLz7ZevPZtJt1sexjSxTRWzjW2uyxZf52Iw
+WUOlVod4XVHMs0DrNtwO8FwYzCDHQ5qeTdQdvvQ9vEAAAAQgHys1d6LlKiRjws2JrctuJj
+2BT8cnSdPmot04q3MRWQz6Y+2sk0IsWCgIY08vl4tveA4DsYajoaMtAhAvOdXr2fnAAAAC
+VpZ29yZHJvemRvdkBJZ29ycy1NYWNCb29rLVByby0yLmxvY2FsAQIDBAU=
+-----END OPENSSH PRIVATE KEY-----
diff --git a/internal/git2go/testdata/signing_ssh_key_ecdsa.pub b/internal/git2go/testdata/signing_ssh_key_ecdsa.pub
new file mode 100644
index 000000000..43cf9d4eb
--- /dev/null
+++ b/internal/git2go/testdata/signing_ssh_key_ecdsa.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBADNDgupT5psvNahGy+MDQkmDGFpWpgwXtxlCH7H1TsTt3Y2QtMaT87wDNcjTHfsL4RdsfdJ8LnMBSUpEEqyacFTeQEzCDl6TSGp6S8+2Xrz2bSbdbHsY0sU0Vs41trssWX+diMFlDpVaHeF1RzLNA6zbcDvBcGMwgx0Oank3UHb70PbxA== user@localhost
diff --git a/internal/git2go/testdata/signing_ssh_key_ed25519 b/internal/git2go/testdata/signing_ssh_key_ed25519
new file mode 100644
index 000000000..81476de35
--- /dev/null
+++ b/internal/git2go/testdata/signing_ssh_key_ed25519
@@ -0,0 +1,8 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACBXMpA2lE+8eKF8lAn4KiYXwF0W4bbC5eH4uNeNs6zwiAAAAKiS/Kcbkvyn
+GwAAAAtzc2gtZWQyNTUxOQAAACBXMpA2lE+8eKF8lAn4KiYXwF0W4bbC5eH4uNeNs6zwiA
+AAAEDhp93OSvbbx0XSDEbfGhI4xna38KQ5DxXDsTK4vjUUAlcykDaUT7x4oXyUCfgqJhfA
+XRbhtsLl4fi4142zrPCIAAAAJWlnb3Jkcm96ZG92QElnb3JzLU1hY0Jvb2stUHJvLTIubG
+9jYWw=
+-----END OPENSSH PRIVATE KEY-----
diff --git a/internal/git2go/testdata/signing_ssh_key_ed25519.pub b/internal/git2go/testdata/signing_ssh_key_ed25519.pub
new file mode 100644
index 000000000..3d19b507c
--- /dev/null
+++ b/internal/git2go/testdata/signing_ssh_key_ed25519.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFcykDaUT7x4oXyUCfgqJhfAXRbhtsLl4fi4142zrPCI user@localhost
diff --git a/internal/git2go/testdata/signing_ssh_key_ed25519.sig b/internal/git2go/testdata/signing_ssh_key_ed25519.sig
new file mode 100644
index 000000000..84e570863
--- /dev/null
+++ b/internal/git2go/testdata/signing_ssh_key_ed25519.sig
@@ -0,0 +1,6 @@
+-----BEGIN SSH SIGNATURE-----
+U1NIU0lHAAAAAQAAADMAAAALc3NoLWVkMjU1MTkAAAAgVzKQNpRPvHihfJQJ+Com
+F8BdFuG2wuXh+LjXjbOs8IgAAAADZ2l0AAAAAAAAAAZzaGE1MTIAAABTAAAAC3Nz
+aC1lZDI1NTE5AAAAQHJNpqX2dyX9A825n1vgvxb4Hf5BiHazRK+O/xRoXZMWaqzh
+khAPOBMw5Shf6FnoSNrwufXXVv6bQqMlxuaDigQ=
+-----END SSH SIGNATURE-----
diff --git a/internal/git2go/testdata/signing_ssh_key_rsa b/internal/git2go/testdata/signing_ssh_key_rsa
new file mode 100644
index 000000000..3ca63ee04
--- /dev/null
+++ b/internal/git2go/testdata/signing_ssh_key_rsa
@@ -0,0 +1,50 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+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
+MEBQ==
+-----END OPENSSH PRIVATE KEY-----
diff --git a/internal/git2go/testdata/signing_ssh_key_rsa.pub b/internal/git2go/testdata/signing_ssh_key_rsa.pub
new file mode 100644
index 000000000..1db22a053
--- /dev/null
+++ b/internal/git2go/testdata/signing_ssh_key_rsa.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDhn0or8bp0JRdMBKhEBeSFf9iWOK6OHyfWqVTT9aRwrEodO4tg1ky96JYbGZLNW3d/t4Qt1sH5AU04oeVrtFNUhoRyakLoYH5ss6VFQBPfsVoFFmDRKYyUmw711RiP1PTzeaPwyuk2DiS+oRrf7dY9i6uSrtBVKakpIqmX0Enk0uUABcAE8+XprTLCTtrl/agAiPlco8nRoBUIss98iJOp3siFPBt3UT+bj2+v/dqVI8iRvDsNJJ73Ys9Y+LDCqIO14qSSU8dZQ+wrQVK7+wE/9z1lGgjF9/NALSyQYBfeTM3wu4xIdiKSPKQDmAM3TUiLbi1ZVMN6+ifEcpKiDaNkuihUsXLCOVRwKnaC731QBzWpJOA3WrvC0dpQQ5fc4BdcmgoditNdIYC26whNiKQi/zjb8LGb/2OnC0Ez3paVDZiNWRAD+Rq/0u4nVmqiytw5lSMmck14wkKNdfiJnj3b4jF+zUE+C1D1T3Oy8wwcIceFfaFgWyPH2PT5+xVRNCczKm6bC7yslU2W/bgDw9tCwzvHUO4FLDg6CMpXWt3xX1s7bQFUV/sjflVbj7mhZit9oUlQyfaqbEOW6YRcjV2CYC+OTXlOguBGcRD2bvo/lCNb9hVIs0DUy6KxDIAWH1Z7+ME0j24rdS5GsVMsz/CyGWUmRNRJxrCsr+USMB9ebQ== user@localhost
diff --git a/internal/git2go/testdata/signing_ssh_key_rsa.sig b/internal/git2go/testdata/signing_ssh_key_rsa.sig
new file mode 100644
index 000000000..947e4f2e3
--- /dev/null
+++ b/internal/git2go/testdata/signing_ssh_key_rsa.sig
@@ -0,0 +1,26 @@
+-----BEGIN SSH SIGNATURE-----
+U1NIU0lHAAAAAQAAAhcAAAAHc3NoLXJzYQAAAAMBAAEAAAIBAOGfSivxunQlF0wE
+qEQF5IV/2JY4ro4fJ9apVNP1pHCsSh07i2DWTL3olhsZks1bd3+3hC3WwfkBTTih
+5Wu0U1SGhHJqQuhgfmyzpUVAE9+xWgUWYNEpjJSbDvXVGI/U9PN5o/DK6TYOJL6h
+Gt/t1j2Lq5Ku0FUpqSkiqZfQSeTS5QAFwATz5emtMsJO2uX9qACI+VyjydGgFQiy
+z3yIk6neyIU8G3dRP5uPb6/92pUjyJG8Ow0knvdiz1j4sMKog7XipJJTx1lD7CtB
+Urv7AT/3PWUaCMX380AtLJBgF95MzfC7jEh2IpI8pAOYAzdNSItuLVlUw3r6J8Ry
+kqINo2S6KFSxcsI5VHAqdoLvfVAHNakk4Ddau8LR2lBDl9zgF1yaCh2K010hgLbr
+CE2IpCL/ONvwsZv/Y6cLQTPelpUNmI1ZEAP5Gr/S7idWaqLK3DmVIyZyTXjCQo11
++ImePdviMX7NQT4LUPVPc7LzDBwhx4V9oWBbI8fY9Pn7FVE0JzMqbpsLvKyVTZb9
+uAPD20LDO8dQ7gUsODoIylda3fFfWzttAVRX+yN+VVuPuaFmK32hSVDJ9qpsQ5bp
+hFyNXYJgL45NeU6C4EZxEPZu+j+UI1v2FUizQNTLorEMgBYfVnv4wTSPbit1Lkax
+UyzP8LIZZSZE1EnGsKyv5RIwH15tAAAAA2dpdAAAAAAAAAAGc2hhNTEyAAACFAAA
+AAxyc2Etc2hhMi01MTIAAAIAnlPz3Ozn2a21NbLHXsbHr5JoUArH4yZB4VQYvEmB
+K8lfeSQeDyEm8+D2Nw0QU+UFjlmdQF41+4yT18ytILZsRJvoupoXyjBGx9nRUUUO
+Pl2X3Da1WRjbfMYpUclv5P+b6j50km2Ax1yODO8GFZmYF9PNCLGL8fdVahSgQTgX
+uAHBnRL5qO0NXUo5t8/KXu1f7vimnOJas/m13YGQhE0aH3KaQbNQteX6y6J12lrd
+37X+DAgPjtw9Otj8zYKLncKTHqAmG7xbfWchjDRlbaahz34MC+qhelU5FutJ25z1
+ifMNYBXxZMcw3G1TKkAdagJ0JDINlYiBitMr1b4wKDwb90mzs11m+PKPUDlk2wa/
+rHx09ZP3rSkT+PVEICbdfaAiWAVx9QQv4X00VosONDe7l6uqfpOcTxnSAieyov8F
+SpdfzXm1eZ7eN3Gbx3QlVInNIek9JYhUheVWgptkYukjGf1XxOUn9N5DuhPjXnuN
+ZUeAlopCc4s1y3uE8W3UnUPxv+XJDYo+1gCAximwfiRJWylWjhByX27yysnyXsq2
+a2WNOUSGxoi1cjpdi7g7qZw4p2p156sFi1hfIk78KlevaMtZiaWQMVMOHKGI4lID
+EJE6to52SLD9VqeRilKyryNdatWlJ0UZhjgENvU8hjnlZE2d/aznolbRSetK4Y1q
+Auw=
+-----END SSH SIGNATURE-----