summaryrefslogtreecommitdiff
path: root/credentials.go
diff options
context:
space:
mode:
authorlhchavez <[email protected]>2020-12-06 11:55:04 -0800
committerGitHub <[email protected]>2020-12-06 11:55:04 -0800
commitabf02bc7d79dfb7b0bbcd404ebecb202cff2a18e (patch)
treeec93caf1ed9238b91e7ec6a1c1e470441860f6fc /credentials.go
parent54afccfa0f5a5574525cbba3b4568cbda252a3df (diff)
Add `NewCredentialSSHKeyFromSigner` (#706)
This change adds `NewCredentialSSHKeyFromSigner`, which allows idiomatic use of SSH keys from Go. This also lets us spin off an SSH server in the tests.
Diffstat (limited to 'credentials.go')
-rw-r--r--credentials.go62
1 files changed, 62 insertions, 0 deletions
diff --git a/credentials.go b/credentials.go
index b1051b9..deb399a 100644
--- a/credentials.go
+++ b/credentials.go
@@ -3,15 +3,20 @@ package git
/*
#include <git2.h>
#include <git2/credential.h>
+#include <git2/sys/credential.h>
git_credential_t _go_git_credential_credtype(git_credential *cred);
+void _go_git_populate_credential_ssh_custom(git_credential_ssh_custom *cred);
*/
import "C"
import (
+ "crypto/rand"
"fmt"
"runtime"
"strings"
"unsafe"
+
+ "golang.org/x/crypto/ssh"
)
// CredentialType is a bitmask of supported credential types.
@@ -192,6 +197,63 @@ func NewCredentialSSHKeyFromAgent(username string) (*Credential, error) {
return cred, nil
}
+type credentialSSHCustomData struct {
+ signer ssh.Signer
+}
+
+//export credentialSSHCustomFree
+func credentialSSHCustomFree(cred *C.git_credential_ssh_custom) {
+ if cred == nil {
+ return
+ }
+
+ C.free(unsafe.Pointer(cred.username))
+ C.free(unsafe.Pointer(cred.publickey))
+ pointerHandles.Untrack(cred.payload)
+ C.free(unsafe.Pointer(cred))
+}
+
+//export credentialSSHSignCallback
+func credentialSSHSignCallback(
+ errorMessage **C.char,
+ sig **C.uchar,
+ sig_len *C.size_t,
+ data *C.uchar,
+ data_len C.size_t,
+ handle unsafe.Pointer,
+) C.int {
+ signer := pointerHandles.Get(handle).(*credentialSSHCustomData).signer
+ signature, err := signer.Sign(rand.Reader, C.GoBytes(unsafe.Pointer(data), C.int(data_len)))
+ if err != nil {
+ return setCallbackError(errorMessage, err)
+ }
+ *sig = (*C.uchar)(C.CBytes(signature.Blob))
+ *sig_len = C.size_t(len(signature.Blob))
+ return C.int(ErrorCodeOK)
+}
+
+// NewCredentialSSHKeyFromSigner creates new SSH credentials using the provided signer.
+func NewCredentialSSHKeyFromSigner(username string, signer ssh.Signer) (*Credential, error) {
+ publicKey := signer.PublicKey().Marshal()
+
+ ccred := (*C.git_credential_ssh_custom)(C.calloc(1, C.size_t(unsafe.Sizeof(C.git_credential_ssh_custom{}))))
+ ccred.parent.credtype = C.GIT_CREDENTIAL_SSH_CUSTOM
+ ccred.username = C.CString(username)
+ ccred.publickey = (*C.char)(C.CBytes(publicKey))
+ ccred.publickey_len = C.size_t(len(publicKey))
+ C._go_git_populate_credential_ssh_custom(ccred)
+
+ data := credentialSSHCustomData{
+ signer: signer,
+ }
+ ccred.payload = pointerHandles.Track(&data)
+
+ cred := newCredential()
+ cred.ptr = &ccred.parent
+
+ return cred, nil
+}
+
func NewCredentialDefault() (*Credential, error) {
runtime.LockOSThread()
defer runtime.UnlockOSThread()