package git /* #include void _go_git_populate_credential_ssh_custom(git_cred_ssh_custom *cred); */ import "C" import ( "crypto/rand" "errors" "fmt" "unsafe" "golang.org/x/crypto/ssh" ) type CredType uint const ( CredTypeUserpassPlaintext CredType = C.GIT_CREDTYPE_USERPASS_PLAINTEXT CredTypeSshKey CredType = C.GIT_CREDTYPE_SSH_KEY CredTypeSshCustom CredType = C.GIT_CREDTYPE_SSH_CUSTOM CredTypeDefault CredType = C.GIT_CREDTYPE_DEFAULT ) type Cred struct { doNotCompare ptr *C.git_cred } func (o *Cred) HasUsername() bool { if C.git_cred_has_username(o.ptr) == 1 { return true } return false } func (o *Cred) Type() CredType { return (CredType)(o.ptr.credtype) } func credFromC(ptr *C.git_cred) *Cred { return &Cred{ptr: ptr} } // GetUserpassPlaintext returns the plaintext username/password combination stored in the Cred. func (o *Cred) GetUserpassPlaintext() (username, password string, err error) { if o.Type() != CredTypeUserpassPlaintext { err = errors.New("credential is not userpass plaintext") return } plaintextCredPtr := (*C.git_cred_userpass_plaintext)(unsafe.Pointer(o.ptr)) username = C.GoString(plaintextCredPtr.username) password = C.GoString(plaintextCredPtr.password) return } // GetSSHKey returns the SSH-specific key information from the Cred object. func (o *Cred) GetSSHKey() (username, publickey, privatekey, passphrase string, err error) { if o.Type() != CredTypeSshKey { err = fmt.Errorf("credential is not an SSH key: %v", o.Type()) return } sshKeyCredPtr := (*C.git_cred_ssh_key)(unsafe.Pointer(o.ptr)) username = C.GoString(sshKeyCredPtr.username) publickey = C.GoString(sshKeyCredPtr.publickey) privatekey = C.GoString(sshKeyCredPtr.privatekey) passphrase = C.GoString(sshKeyCredPtr.passphrase) return } func NewCredUsername(username string) (int, Cred) { cred := Cred{} cusername := C.CString(username) ret := C.git_cred_username_new(&cred.ptr, cusername) return int(ret), cred } func NewCredUserpassPlaintext(username string, password string) (int, Cred) { cred := Cred{} cusername := C.CString(username) defer C.free(unsafe.Pointer(cusername)) cpassword := C.CString(password) defer C.free(unsafe.Pointer(cpassword)) ret := C.git_cred_userpass_plaintext_new(&cred.ptr, cusername, cpassword) return int(ret), cred } // NewCredSshKey creates new ssh credentials reading the public and private keys // from the file system. func NewCredSshKey(username string, publicKeyPath string, privateKeyPath string, passphrase string) (int, Cred) { cred := Cred{} cusername := C.CString(username) defer C.free(unsafe.Pointer(cusername)) cpublickey := C.CString(publicKeyPath) defer C.free(unsafe.Pointer(cpublickey)) cprivatekey := C.CString(privateKeyPath) defer C.free(unsafe.Pointer(cprivatekey)) cpassphrase := C.CString(passphrase) defer C.free(unsafe.Pointer(cpassphrase)) ret := C.git_cred_ssh_key_new(&cred.ptr, cusername, cpublickey, cprivatekey, cpassphrase) return int(ret), cred } // NewCredSshKeyFromMemory creates new ssh credentials using the publicKey and privateKey // arguments as the values for the public and private keys. func NewCredSshKeyFromMemory(username string, publicKey string, privateKey string, passphrase string) (int, Cred) { cred := Cred{} cusername := C.CString(username) defer C.free(unsafe.Pointer(cusername)) cpublickey := C.CString(publicKey) defer C.free(unsafe.Pointer(cpublickey)) cprivatekey := C.CString(privateKey) defer C.free(unsafe.Pointer(cprivatekey)) cpassphrase := C.CString(passphrase) defer C.free(unsafe.Pointer(cpassphrase)) ret := C.git_cred_ssh_key_memory_new(&cred.ptr, cusername, cpublickey, cprivatekey, cpassphrase) return int(ret), cred } func NewCredSshKeyFromAgent(username string) (int, Cred) { cred := Cred{} cusername := C.CString(username) defer C.free(unsafe.Pointer(cusername)) ret := C.git_cred_ssh_key_from_agent(&cred.ptr, cusername) return int(ret), cred } func NewCredDefault() (int, Cred) { cred := Cred{} ret := C.git_cred_default_new(&cred.ptr) return int(ret), cred } type credentialSSHCustomData struct { signer ssh.Signer } //export credentialSSHCustomFree func credentialSSHCustomFree(cred *C.git_cred_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) (*Cred, error) { publicKey := signer.PublicKey().Marshal() ccred := (*C.git_cred_ssh_custom)(C.calloc(1, C.size_t(unsafe.Sizeof(C.git_cred_ssh_custom{})))) ccred.parent.credtype = C.GIT_CREDTYPE_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 := Cred{ ptr: &ccred.parent, } return &cred, nil }