From 29835bb7eea3daae06cad6e5ed25ccaa40fa2912 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miloslav=20Trma=C4=8D?= <mitr@redhat.com>
Date: Fri, 21 Feb 2020 14:21:04 +0100
Subject: [PATCH 1/3] Update to github.com/mtrmac/gpgme v0.1.2
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

This fixes CVE-2020-8945 by incorporating proglottis/gpgme#23 .

Other changes included by the rebase:
- Support for gpgme_off_t (~no-op on Linux)
- Wrapping a few more GPGME functions (irrelevant if we don't call them)

Given how invasive the CVE fix is (affecting basically all binding
code), it seems safer to just update the package (and be verifiably
equivalent with upstream) than to backport and try to back out the few
other changes.

Performed by updating vendor conf,
$ vndr github.com/mtrmac/gpgme
and manually backing out unrelated deletions of files.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
---
 vendor.conf                                   |   2 +-
 vendor/github.com/mtrmac/gpgme/data.go        |  18 +-
 vendor/github.com/mtrmac/gpgme/go.mod         |   3 +
 vendor/github.com/mtrmac/gpgme/go_gpgme.c     |  22 ++
 vendor/github.com/mtrmac/gpgme/go_gpgme.h     |  12 +
 vendor/github.com/mtrmac/gpgme/gpgme.go       | 346 ++++++++++++++----
 .../mtrmac/gpgme/unset_agent_info.go          |  18 +
 .../mtrmac/gpgme/unset_agent_info_windows.go  |  14 +
 8 files changed, 357 insertions(+), 78 deletions(-)
 create mode 100644 vendor/github.com/mtrmac/gpgme/go.mod
 create mode 100644 vendor/github.com/mtrmac/gpgme/unset_agent_info.go
 create mode 100644 vendor/github.com/mtrmac/gpgme/unset_agent_info_windows.go

diff --git a/vendor.conf b/vendor.conf
index a6aa486b..c7b5592f 100644
--- a/vendor.conf
+++ b/vendor.conf
@@ -49,7 +49,7 @@ github.com/xeipuuv/gojsonpointer master
 go4.org master https://github.com/camlistore/go4
 github.com/ostreedev/ostree-go aeb02c6b6aa2889db3ef62f7855650755befd460
 # -- end OCI image validation requirements
-github.com/mtrmac/gpgme master
+github.com/mtrmac/gpgme v0.1.2
 # openshift/origin' k8s dependencies as of OpenShift v1.1.5
 k8s.io/client-go master
 github.com/ghodss/yaml 73d445a93680fa1a78ae23a5839bad48f32ba1ee
diff --git a/vendor/github.com/mtrmac/gpgme/data.go b/vendor/github.com/mtrmac/gpgme/data.go
index eebc9726..eee32c03 100644
--- a/vendor/github.com/mtrmac/gpgme/data.go
+++ b/vendor/github.com/mtrmac/gpgme/data.go
@@ -50,25 +50,25 @@ func gogpgme_writefunc(handle, buffer unsafe.Pointer, size C.size_t) C.ssize_t {
 }
 
 //export gogpgme_seekfunc
-func gogpgme_seekfunc(handle unsafe.Pointer, offset C.off_t, whence C.int) C.off_t {
+func gogpgme_seekfunc(handle unsafe.Pointer, offset C.gpgme_off_t, whence C.int) C.gpgme_off_t {
 	d := callbackLookup(uintptr(handle)).(*Data)
 	n, err := d.s.Seek(int64(offset), int(whence))
 	if err != nil {
 		C.gpgme_err_set_errno(C.EIO)
 		return -1
 	}
-	return C.off_t(n)
+	return C.gpgme_off_t(n)
 }
 
 // The Data buffer used to communicate with GPGME
 type Data struct {
-	dh  C.gpgme_data_t
+	dh  C.gpgme_data_t // WARNING: Call runtime.KeepAlive(d) after ANY passing of d.dh to C
 	buf []byte
 	cbs C.struct_gpgme_data_cbs
 	r   io.Reader
 	w   io.Writer
 	s   io.Seeker
-	cbc uintptr
+	cbc uintptr // WARNING: Call runtime.KeepAlive(d) after ANY use of d.cbc in C (typically via d.dh)
 }
 
 func newData() *Data {
@@ -154,12 +154,14 @@ func (d *Data) Close() error {
 		callbackDelete(d.cbc)
 	}
 	_, err := C.gpgme_data_release(d.dh)
+	runtime.KeepAlive(d)
 	d.dh = nil
 	return err
 }
 
 func (d *Data) Write(p []byte) (int, error) {
 	n, err := C.gpgme_data_write(d.dh, unsafe.Pointer(&p[0]), C.size_t(len(p)))
+	runtime.KeepAlive(d)
 	if err != nil {
 		return 0, err
 	}
@@ -171,6 +173,7 @@ func (d *Data) Write(p []byte) (int, error) {
 
 func (d *Data) Read(p []byte) (int, error) {
 	n, err := C.gpgme_data_read(d.dh, unsafe.Pointer(&p[0]), C.size_t(len(p)))
+	runtime.KeepAlive(d)
 	if err != nil {
 		return 0, err
 	}
@@ -181,11 +184,14 @@ func (d *Data) Read(p []byte) (int, error) {
 }
 
 func (d *Data) Seek(offset int64, whence int) (int64, error) {
-	n, err := C.gpgme_data_seek(d.dh, C.off_t(offset), C.int(whence))
+	n, err := C.gogpgme_data_seek(d.dh, C.gpgme_off_t(offset), C.int(whence))
+	runtime.KeepAlive(d)
 	return int64(n), err
 }
 
 // Name returns the associated filename if any
 func (d *Data) Name() string {
-	return C.GoString(C.gpgme_data_get_file_name(d.dh))
+	res := C.GoString(C.gpgme_data_get_file_name(d.dh))
+	runtime.KeepAlive(d)
+	return res
 }
diff --git a/vendor/github.com/mtrmac/gpgme/go.mod b/vendor/github.com/mtrmac/gpgme/go.mod
new file mode 100644
index 00000000..3dd09c9f
--- /dev/null
+++ b/vendor/github.com/mtrmac/gpgme/go.mod
@@ -0,0 +1,3 @@
+module github.com/mtrmac/gpgme
+
+go 1.11
diff --git a/vendor/github.com/mtrmac/gpgme/go_gpgme.c b/vendor/github.com/mtrmac/gpgme/go_gpgme.c
index b887574e..00da3ab3 100644
--- a/vendor/github.com/mtrmac/gpgme/go_gpgme.c
+++ b/vendor/github.com/mtrmac/gpgme/go_gpgme.c
@@ -8,6 +8,28 @@ void gogpgme_set_passphrase_cb(gpgme_ctx_t ctx, gpgme_passphrase_cb_t cb, uintpt
 	gpgme_set_passphrase_cb(ctx, cb, (void *)handle);
 }
 
+gpgme_off_t gogpgme_data_seek(gpgme_data_t dh, gpgme_off_t offset, int whence) {
+	return gpgme_data_seek(dh, offset, whence);
+}
+
+gpgme_error_t gogpgme_op_assuan_transact_ext(
+		gpgme_ctx_t ctx,
+		char* cmd,
+		uintptr_t data_h,
+		uintptr_t inquiry_h,
+		uintptr_t status_h,
+		gpgme_error_t *operr
+	){
+	return gpgme_op_assuan_transact_ext(
+		ctx,
+		cmd,
+		(gpgme_assuan_data_cb_t)    gogpgme_assuan_data_callback,    (void *)data_h,
+		(gpgme_assuan_inquire_cb_t) gogpgme_assuan_inquiry_callback, (void *)inquiry_h,
+		(gpgme_assuan_status_cb_t)  gogpgme_assuan_status_callback,  (void *)status_h,
+		operr
+	);
+}
+
 unsigned int key_revoked(gpgme_key_t k) {
 	return k->revoked;
 }
diff --git a/vendor/github.com/mtrmac/gpgme/go_gpgme.h b/vendor/github.com/mtrmac/gpgme/go_gpgme.h
index a3678b12..d4826ab3 100644
--- a/vendor/github.com/mtrmac/gpgme/go_gpgme.h
+++ b/vendor/github.com/mtrmac/gpgme/go_gpgme.h
@@ -6,12 +6,24 @@
 
 #include <gpgme.h>
 
+/* GPGME_VERSION_NUMBER was introduced in 1.4.0 */
+#if !defined(GPGME_VERSION_NUMBER) || GPGME_VERSION_NUMBER < 0x010402
+typedef off_t gpgme_off_t; /* Introduced in 1.4.2 */
+#endif
+
 extern ssize_t gogpgme_readfunc(void *handle, void *buffer, size_t size);
 extern ssize_t gogpgme_writefunc(void *handle, void *buffer, size_t size);
 extern off_t gogpgme_seekfunc(void *handle, off_t offset, int whence);
 extern gpgme_error_t gogpgme_passfunc(void *hook, char *uid_hint, char *passphrase_info, int prev_was_bad, int fd);
 extern gpgme_error_t gogpgme_data_new_from_cbs(gpgme_data_t *dh, gpgme_data_cbs_t cbs, uintptr_t handle);
 extern void gogpgme_set_passphrase_cb(gpgme_ctx_t ctx, gpgme_passphrase_cb_t cb, uintptr_t handle);
+extern gpgme_off_t gogpgme_data_seek(gpgme_data_t dh, gpgme_off_t offset, int whence);
+
+extern gpgme_error_t gogpgme_op_assuan_transact_ext(gpgme_ctx_t ctx, char *cmd, uintptr_t data_h, uintptr_t inquiry_h , uintptr_t status_h, gpgme_error_t *operr);
+
+extern gpgme_error_t gogpgme_assuan_data_callback(void *opaque, void* data, size_t datalen );
+extern gpgme_error_t gogpgme_assuan_inquiry_callback(void *opaque, char* name, char* args);
+extern gpgme_error_t gogpgme_assuan_status_callback(void *opaque, char* status, char* args);
 
 extern unsigned int key_revoked(gpgme_key_t k);
 extern unsigned int key_expired(gpgme_key_t k);
diff --git a/vendor/github.com/mtrmac/gpgme/gpgme.go b/vendor/github.com/mtrmac/gpgme/gpgme.go
index 20aad737..c19b9aeb 100644
--- a/vendor/github.com/mtrmac/gpgme/gpgme.go
+++ b/vendor/github.com/mtrmac/gpgme/gpgme.go
@@ -7,7 +7,6 @@ package gpgme
 // #include <gpgme.h>
 // #include "go_gpgme.h"
 import "C"
-
 import (
 	"fmt"
 	"io"
@@ -48,9 +47,8 @@ const (
 	ProtocolAssuan   Protocol = C.GPGME_PROTOCOL_ASSUAN
 	ProtocolG13      Protocol = C.GPGME_PROTOCOL_G13
 	ProtocolUIServer Protocol = C.GPGME_PROTOCOL_UISERVER
-	// ProtocolSpawn    Protocol = C.GPGME_PROTOCOL_SPAWN // Unavailable in 1.4.3
-	ProtocolDefault Protocol = C.GPGME_PROTOCOL_DEFAULT
-	ProtocolUnknown Protocol = C.GPGME_PROTOCOL_UNKNOWN
+	ProtocolDefault  Protocol = C.GPGME_PROTOCOL_DEFAULT
+	ProtocolUnknown  Protocol = C.GPGME_PROTOCOL_UNKNOWN
 )
 
 type PinEntryMode int
@@ -70,7 +68,6 @@ const (
 	EncryptNoEncryptTo EncryptFlag = C.GPGME_ENCRYPT_NO_ENCRYPT_TO
 	EncryptPrepare     EncryptFlag = C.GPGME_ENCRYPT_PREPARE
 	EncryptExceptSign  EncryptFlag = C.GPGME_ENCRYPT_EXPECT_SIGN
-	// EncryptNoCompress  EncryptFlag = C.GPGME_ENCRYPT_NO_COMPRESS // Unavailable in 1.4.3
 )
 
 type HashAlgo int
@@ -84,7 +81,6 @@ const (
 	KeyListModeExtern       KeyListMode = C.GPGME_KEYLIST_MODE_EXTERN
 	KeyListModeSigs         KeyListMode = C.GPGME_KEYLIST_MODE_SIGS
 	KeyListModeSigNotations KeyListMode = C.GPGME_KEYLIST_MODE_SIG_NOTATIONS
-	// KeyListModeWithSecret   KeyListMode = C.GPGME_KEYLIST_MODE_WITH_SECRET // Unavailable in 1.4.3
 	KeyListModeEphemeral    KeyListMode = C.GPGME_KEYLIST_MODE_EPHEMERAL
 	KeyListModeModeValidate KeyListMode = C.GPGME_KEYLIST_MODE_VALIDATE
 )
@@ -168,39 +164,60 @@ func EngineCheckVersion(p Protocol) error {
 }
 
 type EngineInfo struct {
-	info C.gpgme_engine_info_t
+	next            *EngineInfo
+	protocol        Protocol
+	fileName        string
+	homeDir         string
+	version         string
+	requiredVersion string
+}
+
+func copyEngineInfo(info C.gpgme_engine_info_t) *EngineInfo {
+	res := &EngineInfo{
+		next:            nil,
+		protocol:        Protocol(info.protocol),
+		fileName:        C.GoString(info.file_name),
+		homeDir:         C.GoString(info.home_dir),
+		version:         C.GoString(info.version),
+		requiredVersion: C.GoString(info.req_version),
+	}
+	if info.next != nil {
+		res.next = copyEngineInfo(info.next)
+	}
+	return res
 }
 
 func (e *EngineInfo) Next() *EngineInfo {
-	if e.info.next == nil {
-		return nil
-	}
-	return &EngineInfo{info: e.info.next}
+	return e.next
 }
 
 func (e *EngineInfo) Protocol() Protocol {
-	return Protocol(e.info.protocol)
+	return e.protocol
 }
 
 func (e *EngineInfo) FileName() string {
-	return C.GoString(e.info.file_name)
+	return e.fileName
 }
 
 func (e *EngineInfo) Version() string {
-	return C.GoString(e.info.version)
+	return e.version
 }
 
 func (e *EngineInfo) RequiredVersion() string {
-	return C.GoString(e.info.req_version)
+	return e.requiredVersion
 }
 
 func (e *EngineInfo) HomeDir() string {
-	return C.GoString(e.info.home_dir)
+	return e.homeDir
 }
 
 func GetEngineInfo() (*EngineInfo, error) {
-	info := &EngineInfo{}
-	return info, handleError(C.gpgme_get_engine_info(&info.info))
+	var cInfo C.gpgme_engine_info_t
+	err := handleError(C.gpgme_get_engine_info(&cInfo))
+	if err != nil {
+		return nil, err
+	}
+	return copyEngineInfo(cInfo), nil // It is up to the caller not to invalidate cInfo concurrently until this is done.
 }
 
 func SetEngineInfo(proto Protocol, fileName, homeDir string) error {
@@ -261,9 +278,9 @@ type Context struct {
 	KeyError error
 
 	callback Callback
-	cbc      uintptr
+	cbc      uintptr // WARNING: Call runtime.KeepAlive(c) after ANY use of c.cbc in C (typically via c.ctx)
 
-	ctx C.gpgme_ctx_t
+	ctx C.gpgme_ctx_t // WARNING: Call runtime.KeepAlive(c) after ANY passing of c.ctx to C
 }
 
 func New() (*Context, error) {
@@ -281,49 +298,68 @@ func (c *Context) Release() {
 		callbackDelete(c.cbc)
 	}
 	C.gpgme_release(c.ctx)
+	runtime.KeepAlive(c)
 	c.ctx = nil
 }
 
 func (c *Context) SetArmor(yes bool) {
 	C.gpgme_set_armor(c.ctx, cbool(yes))
+	runtime.KeepAlive(c)
 }
 
 func (c *Context) Armor() bool {
-	return C.gpgme_get_armor(c.ctx) != 0
+	res := C.gpgme_get_armor(c.ctx) != 0
+	runtime.KeepAlive(c)
+	return res
 }
 
 func (c *Context) SetTextMode(yes bool) {
 	C.gpgme_set_textmode(c.ctx, cbool(yes))
+	runtime.KeepAlive(c)
 }
 
 func (c *Context) TextMode() bool {
-	return C.gpgme_get_textmode(c.ctx) != 0
+	res := C.gpgme_get_textmode(c.ctx) != 0
+	runtime.KeepAlive(c)
+	return res
 }
 
 func (c *Context) SetProtocol(p Protocol) error {
-	return handleError(C.gpgme_set_protocol(c.ctx, C.gpgme_protocol_t(p)))
+	err := handleError(C.gpgme_set_protocol(c.ctx, C.gpgme_protocol_t(p)))
+	runtime.KeepAlive(c)
+	return err
 }
 
 func (c *Context) Protocol() Protocol {
-	return Protocol(C.gpgme_get_protocol(c.ctx))
+	res := Protocol(C.gpgme_get_protocol(c.ctx))
+	runtime.KeepAlive(c)
+	return res
 }
 
 func (c *Context) SetKeyListMode(m KeyListMode) error {
-	return handleError(C.gpgme_set_keylist_mode(c.ctx, C.gpgme_keylist_mode_t(m)))
+	err := handleError(C.gpgme_set_keylist_mode(c.ctx, C.gpgme_keylist_mode_t(m)))
+	runtime.KeepAlive(c)
+	return err
 }
 
 func (c *Context) KeyListMode() KeyListMode {
-	return KeyListMode(C.gpgme_get_keylist_mode(c.ctx))
+	res := KeyListMode(C.gpgme_get_keylist_mode(c.ctx))
+	runtime.KeepAlive(c)
+	return res
 }
 
 // Unavailable in 1.3.2:
 // func (c *Context) SetPinEntryMode(m PinEntryMode) error {
-// 	return handleError(C.gpgme_set_pinentry_mode(c.ctx, C.gpgme_pinentry_mode_t(m)))
+// 	err := handleError(C.gpgme_set_pinentry_mode(c.ctx, C.gpgme_pinentry_mode_t(m)))
+// 	runtime.KeepAlive(c)
+// 	return err
 // }
 
 // Unavailable in 1.3.2:
 // func (c *Context) PinEntryMode() PinEntryMode {
-// 	return PinEntryMode(C.gpgme_get_pinentry_mode(c.ctx))
+// 	res := PinEntryMode(C.gpgme_get_pinentry_mode(c.ctx))
+// 	runtime.KeepAlive(c)
+// 	return res
 // }
 
 func (c *Context) SetCallback(callback Callback) error {
@@ -340,11 +376,17 @@ func (c *Context) SetCallback(callback Callback) error {
 		c.cbc = 0
 		_, err = C.gogpgme_set_passphrase_cb(c.ctx, nil, 0)
 	}
+	runtime.KeepAlive(c)
 	return err
 }
 
 func (c *Context) EngineInfo() *EngineInfo {
-	return &EngineInfo{info: C.gpgme_ctx_get_engine_info(c.ctx)}
+	cInfo := C.gpgme_ctx_get_engine_info(c.ctx)
+	runtime.KeepAlive(c)
+	// NOTE: c must be live as long as we are accessing cInfo.
+	res := copyEngineInfo(cInfo)
+	runtime.KeepAlive(c) // for accesses to cInfo
+	return res
 }
 
 func (c *Context) SetEngineInfo(proto Protocol, fileName, homeDir string) error {
@@ -357,19 +399,23 @@ func (c *Context) SetEngineInfo(proto Protocol, fileName, homeDir string) error
 		chome = C.CString(homeDir)
 		defer C.free(unsafe.Pointer(chome))
 	}
-	return handleError(C.gpgme_ctx_set_engine_info(c.ctx, C.gpgme_protocol_t(proto), cfn, chome))
+	err := handleError(C.gpgme_ctx_set_engine_info(c.ctx, C.gpgme_protocol_t(proto), cfn, chome))
+	runtime.KeepAlive(c)
+	return err
 }
 
 func (c *Context) KeyListStart(pattern string, secretOnly bool) error {
 	cpattern := C.CString(pattern)
 	defer C.free(unsafe.Pointer(cpattern))
-	err := C.gpgme_op_keylist_start(c.ctx, cpattern, cbool(secretOnly))
-	return handleError(err)
+	err := handleError(C.gpgme_op_keylist_start(c.ctx, cpattern, cbool(secretOnly)))
+	runtime.KeepAlive(c)
+	return err
 }
 
 func (c *Context) KeyListNext() bool {
 	c.Key = newKey()
 	err := handleError(C.gpgme_op_keylist_next(c.ctx, &c.Key.k))
+	runtime.KeepAlive(c) // implies runtime.KeepAlive(c.Key)
 	if err != nil {
 		if e, ok := err.(Error); ok && e.Code() == ErrorEOF {
 			c.KeyError = nil
@@ -383,7 +429,9 @@ func (c *Context) KeyListNext() bool {
 }
 
 func (c *Context) KeyListEnd() error {
-	return handleError(C.gpgme_op_keylist_end(c.ctx))
+	err := handleError(C.gpgme_op_keylist_end(c.ctx))
+	runtime.KeepAlive(c)
+	return err
 }
 
 func (c *Context) GetKey(fingerprint string, secret bool) (*Key, error) {
@@ -391,7 +439,11 @@ func (c *Context) GetKey(fingerprint string, secret bool) (*Key, error) {
 	cfpr := C.CString(fingerprint)
 	defer C.free(unsafe.Pointer(cfpr))
 	err := handleError(C.gpgme_get_key(c.ctx, cfpr, &key.k, cbool(secret)))
-	if e, ok := err.(Error); key.k == nil && ok && e.Code() == ErrorEOF {
+	runtime.KeepAlive(c)
+	runtime.KeepAlive(key)
+	keyKIsNil := key.k == nil
+	runtime.KeepAlive(key)
+	if e, ok := err.(Error); keyKIsNil && ok && e.Code() == ErrorEOF {
 		return nil, fmt.Errorf("key %q not found", fingerprint)
 	}
 	if err != nil {
@@ -401,11 +453,19 @@ func (c *Context) GetKey(fingerprint string, secret bool) (*Key, error) {
 }
 
 func (c *Context) Decrypt(ciphertext, plaintext *Data) error {
-	return handleError(C.gpgme_op_decrypt(c.ctx, ciphertext.dh, plaintext.dh))
+	err := handleError(C.gpgme_op_decrypt(c.ctx, ciphertext.dh, plaintext.dh))
+	runtime.KeepAlive(c)
+	runtime.KeepAlive(ciphertext)
+	runtime.KeepAlive(plaintext)
+	return err
 }
 
 func (c *Context) DecryptVerify(ciphertext, plaintext *Data) error {
-	return handleError(C.gpgme_op_decrypt_verify(c.ctx, ciphertext.dh, plaintext.dh))
+	err := handleError(C.gpgme_op_decrypt_verify(c.ctx, ciphertext.dh, plaintext.dh))
+	runtime.KeepAlive(c)
+	runtime.KeepAlive(ciphertext)
+	runtime.KeepAlive(plaintext)
+	return err
 }
 
 type Signature struct {
@@ -432,10 +492,20 @@ func (c *Context) Verify(sig, signedText, plain *Data) (string, []Signature, err
 		plainPtr = plain.dh
 	}
 	err := handleError(C.gpgme_op_verify(c.ctx, sig.dh, signedTextPtr, plainPtr))
+	runtime.KeepAlive(c)
+	runtime.KeepAlive(sig)
+	if signedText != nil {
+		runtime.KeepAlive(signedText)
+	}
+	if plain != nil {
+		runtime.KeepAlive(plain)
+	}
 	if err != nil {
 		return "", nil, err
 	}
 	res := C.gpgme_op_verify_result(c.ctx)
+	runtime.KeepAlive(c)
+	// NOTE: c must be live as long as we are accessing res.
 	sigs := []Signature{}
 	for s := res.signatures; s != nil; s = s.next {
 		sig := Signature{
@@ -455,7 +525,9 @@ func (c *Context) Verify(sig, signedText, plain *Data) (string, []Signature, err
 		}
 		sigs = append(sigs, sig)
 	}
-	return C.GoString(res.file_name), sigs, nil
+	fileName := C.GoString(res.file_name)
+	runtime.KeepAlive(c) // for all accesses to res above
+	return fileName, sigs, nil
 }
 
 func (c *Context) Encrypt(recipients []*Key, flags EncryptFlag, plaintext, ciphertext *Data) error {
@@ -467,18 +539,116 @@ func (c *Context) Encrypt(recipients []*Key, flags EncryptFlag, plaintext, ciphe
 		*ptr = recipients[i].k
 	}
 	err := C.gpgme_op_encrypt(c.ctx, (*C.gpgme_key_t)(recp), C.gpgme_encrypt_flags_t(flags), plaintext.dh, ciphertext.dh)
+	runtime.KeepAlive(c)
+	runtime.KeepAlive(recipients)
+	runtime.KeepAlive(plaintext)
+	runtime.KeepAlive(ciphertext)
 	return handleError(err)
 }
 
 func (c *Context) Sign(signers []*Key, plain, sig *Data, mode SigMode) error {
 	C.gpgme_signers_clear(c.ctx)
+	runtime.KeepAlive(c)
 	for _, k := range signers {
-		if err := handleError(C.gpgme_signers_add(c.ctx, k.k)); err != nil {
+		err := handleError(C.gpgme_signers_add(c.ctx, k.k))
+		runtime.KeepAlive(c)
+		runtime.KeepAlive(k)
+		if err != nil {
 			C.gpgme_signers_clear(c.ctx)
+			runtime.KeepAlive(c)
 			return err
 		}
 	}
-	return handleError(C.gpgme_op_sign(c.ctx, plain.dh, sig.dh, C.gpgme_sig_mode_t(mode)))
+	err := handleError(C.gpgme_op_sign(c.ctx, plain.dh, sig.dh, C.gpgme_sig_mode_t(mode)))
+	runtime.KeepAlive(c)
+	runtime.KeepAlive(plain)
+	runtime.KeepAlive(sig)
+	return err
+}
+
+type AssuanDataCallback func(data []byte) error
+type AssuanInquireCallback func(name, args string) error
+type AssuanStatusCallback func(status, args string) error
+
+// AssuanSend sends a raw Assuan command to gpg-agent
+func (c *Context) AssuanSend(
+	cmd string,
+	data AssuanDataCallback,
+	inquiry AssuanInquireCallback,
+	status AssuanStatusCallback,
+) error {
+	var operr C.gpgme_error_t
+
+	dataPtr := callbackAdd(&data)
+	inquiryPtr := callbackAdd(&inquiry)
+	statusPtr := callbackAdd(&status)
+	cmdCStr := C.CString(cmd)
+	defer C.free(unsafe.Pointer(cmdCStr))
+	err := C.gogpgme_op_assuan_transact_ext(
+		c.ctx,
+		cmdCStr,
+		C.uintptr_t(dataPtr),
+		C.uintptr_t(inquiryPtr),
+		C.uintptr_t(statusPtr),
+		&operr,
+	)
+	runtime.KeepAlive(c)
+
+	if handleError(operr) != nil {
+		return handleError(operr)
+	}
+	return handleError(err)
+}
+
+//export gogpgme_assuan_data_callback
+func gogpgme_assuan_data_callback(handle unsafe.Pointer, data unsafe.Pointer, datalen C.size_t) C.gpgme_error_t {
+	c := callbackLookup(uintptr(handle)).(*AssuanDataCallback)
+	if *c == nil {
+		return 0
+	}
+	(*c)(C.GoBytes(data, C.int(datalen)))
+	return 0
+}
+
+//export gogpgme_assuan_inquiry_callback
+func gogpgme_assuan_inquiry_callback(handle unsafe.Pointer, cName *C.char, cArgs *C.char) C.gpgme_error_t {
+	name := C.GoString(cName)
+	args := C.GoString(cArgs)
+	c := callbackLookup(uintptr(handle)).(*AssuanInquireCallback)
+	if *c == nil {
+		return 0
+	}
+	(*c)(name, args)
+	return 0
+}
+
+//export gogpgme_assuan_status_callback
+func gogpgme_assuan_status_callback(handle unsafe.Pointer, cStatus *C.char, cArgs *C.char) C.gpgme_error_t {
+	status := C.GoString(cStatus)
+	args := C.GoString(cArgs)
+	c := callbackLookup(uintptr(handle)).(*AssuanStatusCallback)
+	if *c == nil {
+		return 0
+	}
+	(*c)(status, args)
+	return 0
+}
+
+// ExportModeFlags defines how keys are exported from Export
+type ExportModeFlags uint
+
+const (
+	ExportModeExtern  ExportModeFlags = C.GPGME_EXPORT_MODE_EXTERN
+	ExportModeMinimal ExportModeFlags = C.GPGME_EXPORT_MODE_MINIMAL
+)
+
+func (c *Context) Export(pattern string, mode ExportModeFlags, data *Data) error {
+	pat := C.CString(pattern)
+	defer C.free(unsafe.Pointer(pat))
+	err := handleError(C.gpgme_op_export(c.ctx, pat, C.gpgme_export_mode_t(mode), data.dh))
+	runtime.KeepAlive(c)
+	runtime.KeepAlive(data)
+	return err
 }
 
 // ImportStatusFlags describes the type of ImportStatus.Status. The C API in gpgme.h simply uses "unsigned".
@@ -517,10 +687,14 @@ type ImportResult struct {
 
 func (c *Context) Import(keyData *Data) (*ImportResult, error) {
 	err := handleError(C.gpgme_op_import(c.ctx, keyData.dh))
+	runtime.KeepAlive(c)
+	runtime.KeepAlive(keyData)
 	if err != nil {
 		return nil, err
 	}
 	res := C.gpgme_op_import_result(c.ctx)
+	runtime.KeepAlive(c)
+	// NOTE: c must be live as long as we are accessing res.
 	imports := []ImportStatus{}
 	for s := res.imports; s != nil; s = s.next {
 		imports = append(imports, ImportStatus{
@@ -529,7 +703,7 @@ func (c *Context) Import(keyData *Data) (*ImportResult, error) {
 			Status:      ImportStatusFlags(s.status),
 		})
 	}
-	return &ImportResult{
+	importResult := &ImportResult{
 		Considered:      int(res.considered),
 		NoUserID:        int(res.no_user_id),
 		Imported:        int(res.imported),
@@ -544,11 +718,13 @@ func (c *Context) Import(keyData *Data) (*ImportResult, error) {
 		SecretUnchanged: int(res.secret_unchanged),
 		NotImported:     int(res.not_imported),
 		Imports:         imports,
-	}, nil
+	}
+	runtime.KeepAlive(c) // for all accesses to res above
+	return importResult, nil
 }
 
 type Key struct {
-	k C.gpgme_key_t
+	k C.gpgme_key_t // WARNING: Call Runtime.KeepAlive(k) after ANY passing of k.k to C
 }
 
 func newKey() *Key {
@@ -559,85 +735,122 @@ func newKey() *Key {
 
 func (k *Key) Release() {
 	C.gpgme_key_release(k.k)
+	runtime.KeepAlive(k)
 	k.k = nil
 }
 
 func (k *Key) Revoked() bool {
-	return C.key_revoked(k.k) != 0
+	res := C.key_revoked(k.k) != 0
+	runtime.KeepAlive(k)
+	return res
 }
 
 func (k *Key) Expired() bool {
-	return C.key_expired(k.k) != 0
+	res := C.key_expired(k.k) != 0
+	runtime.KeepAlive(k)
+	return res
 }
 
 func (k *Key) Disabled() bool {
-	return C.key_disabled(k.k) != 0
+	res := C.key_disabled(k.k) != 0
+	runtime.KeepAlive(k)
+	return res
 }
 
 func (k *Key) Invalid() bool {
-	return C.key_invalid(k.k) != 0
+	res := C.key_invalid(k.k) != 0
+	runtime.KeepAlive(k)
+	return res
 }
 
 func (k *Key) CanEncrypt() bool {
-	return C.key_can_encrypt(k.k) != 0
+	res := C.key_can_encrypt(k.k) != 0
+	runtime.KeepAlive(k)
+	return res
 }
 
 func (k *Key) CanSign() bool {
-	return C.key_can_sign(k.k) != 0
+	res := C.key_can_sign(k.k) != 0
+	runtime.KeepAlive(k)
+	return res
 }
 
 func (k *Key) CanCertify() bool {
-	return C.key_can_certify(k.k) != 0
+	res := C.key_can_certify(k.k) != 0
+	runtime.KeepAlive(k)
+	return res
 }
 
 func (k *Key) Secret() bool {
-	return C.key_secret(k.k) != 0
+	res := C.key_secret(k.k) != 0
+	runtime.KeepAlive(k)
+	return res
 }
 
 func (k *Key) CanAuthenticate() bool {
-	return C.key_can_authenticate(k.k) != 0
+	res := C.key_can_authenticate(k.k) != 0
+	runtime.KeepAlive(k)
+	return res
 }
 
 func (k *Key) IsQualified() bool {
-	return C.key_is_qualified(k.k) != 0
+	res := C.key_is_qualified(k.k) != 0
+	runtime.KeepAlive(k)
+	return res
 }
 
 func (k *Key) Protocol() Protocol {
-	return Protocol(k.k.protocol)
+	res := Protocol(k.k.protocol)
+	runtime.KeepAlive(k)
+	return res
 }
 
 func (k *Key) IssuerSerial() string {
-	return C.GoString(k.k.issuer_serial)
+	res := C.GoString(k.k.issuer_serial)
+	runtime.KeepAlive(k)
+	return res
 }
 
 func (k *Key) IssuerName() string {
-	return C.GoString(k.k.issuer_name)
+	res := C.GoString(k.k.issuer_name)
+	runtime.KeepAlive(k)
+	return res
 }
 
 func (k *Key) ChainID() string {
-	return C.GoString(k.k.chain_id)
+	res := C.GoString(k.k.chain_id)
+	runtime.KeepAlive(k)
+	return res
 }
 
 func (k *Key) OwnerTrust() Validity {
-	return Validity(k.k.owner_trust)
+	res := Validity(k.k.owner_trust)
+	runtime.KeepAlive(k)
+	return res
 }
 
 func (k *Key) SubKeys() *SubKey {
-	if k.k.subkeys == nil {
+	subKeys := k.k.subkeys
+	runtime.KeepAlive(k)
+	if subKeys == nil {
 		return nil
 	}
-	return &SubKey{k: k.k.subkeys, parent: k}
+	return &SubKey{k: subKeys, parent: k} // The parent: k reference ensures subKeys remains valid
 }
 
 func (k *Key) UserIDs() *UserID {
-	if k.k.uids == nil {
+	uids := k.k.uids
+	runtime.KeepAlive(k)
+	if uids == nil {
 		return nil
 	}
-	return &UserID{u: k.k.uids, parent: k}
+	return &UserID{u: uids, parent: k} // The parent: k reference ensures uids remains valid
 }
 
 func (k *Key) KeyListMode() KeyListMode {
-	return KeyListMode(k.k.keylist_mode)
+	res := KeyListMode(k.k.keylist_mode)
+	runtime.KeepAlive(k)
+	return res
 }
 
 type SubKey struct {
@@ -737,12 +950,3 @@ func (u *UserID) Comment() string {
 func (u *UserID) Email() string {
 	return C.GoString(u.u.email)
 }
-
-// This is somewhat of a horrible hack. We need to unset GPG_AGENT_INFO so that gpgme does not pass --use-agent to GPG.
-// os.Unsetenv should be enough, but that only calls the underlying C library (which gpgme uses) if cgo is involved
-// - and cgo can't be used in tests. So, provide this helper for test initialization.
-func unsetenvGPGAgentInfo() {
-	v := C.CString("GPG_AGENT_INFO")
-	defer C.free(unsafe.Pointer(v))
-	C.unsetenv(v)
-}
diff --git a/vendor/github.com/mtrmac/gpgme/unset_agent_info.go b/vendor/github.com/mtrmac/gpgme/unset_agent_info.go
new file mode 100644
index 00000000..986aca59
--- /dev/null
+++ b/vendor/github.com/mtrmac/gpgme/unset_agent_info.go
@@ -0,0 +1,18 @@
+// +build !windows
+
+package gpgme
+
+// #include <stdlib.h>
+import "C"
+import (
+	"unsafe"
+)
+
+// This is somewhat of a horrible hack. We need to unset GPG_AGENT_INFO so that gpgme does not pass --use-agent to GPG.
+// os.Unsetenv should be enough, but that only calls the underlying C library (which gpgme uses) if cgo is involved
+// - and cgo can't be used in tests. So, provide this helper for test initialization.
+func unsetenvGPGAgentInfo() {
+	v := C.CString("GPG_AGENT_INFO")
+	defer C.free(unsafe.Pointer(v))
+	C.unsetenv(v)
+}
diff --git a/vendor/github.com/mtrmac/gpgme/unset_agent_info_windows.go b/vendor/github.com/mtrmac/gpgme/unset_agent_info_windows.go
new file mode 100644
index 00000000..431ec86d
--- /dev/null
+++ b/vendor/github.com/mtrmac/gpgme/unset_agent_info_windows.go
@@ -0,0 +1,14 @@
+package gpgme
+
+// #include <stdlib.h>
+import "C"
+import (
+	"unsafe"
+)
+
+// unsetenv is not available in mingw
+func unsetenvGPGAgentInfo() {
+	v := C.CString("GPG_AGENT_INFO=")
+	defer C.free(unsafe.Pointer(v))
+	C.putenv(v)
+}

From 77d5469d2b04509a0cd2dcbe577c6794c7592cbf Mon Sep 17 00:00:00 2001
From: Giuseppe Scrivano <gscrivan@redhat.com>
Date: Wed, 30 Oct 2019 08:12:48 +0100
Subject: [PATCH 2/3] Dockerfile: use golang-github-cpuguy83-go-md2man

the package was renamed on Fedora 31.

Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
---
 Dockerfile | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Dockerfile b/Dockerfile
index 3cf2be4f..56e70daf 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,6 +1,6 @@
 FROM fedora
 
-RUN dnf -y update && dnf install -y make git golang golang-github-cpuguy83-go-md2man \
+RUN dnf -y update && dnf install -y make git golang golang-github-cpuguy83-md2man \
 	# storage deps
 	btrfs-progs-devel \
 	device-mapper-devel \

From 25629ee9722cc491d2dc2bbb224330ca3b21cc25 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miloslav=20Trma=C4=8D?= <mitr@redhat.com>
Date: Mon, 10 Jun 2019 20:48:54 +0200
Subject: [PATCH 3/3] Explicitly disable encrypting test GPG keys
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Since GPG 2.1, GPG asks for a passphrase by default; opt out when
generating test keys to avoid
> gpg: agent_genkey failed: No pinentry
> gpg: key generation failed: No pinentry
which happens otherwise (and we can't use an interactive pinentry
in a batch process anyway).

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
---
 integration/copy_test.go    | 4 ++--
 integration/signing_test.go | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/integration/copy_test.go b/integration/copy_test.go
index d2e979ba..19bf365a 100644
--- a/integration/copy_test.go
+++ b/integration/copy_test.go
@@ -14,7 +14,7 @@ import (
 	"github.com/containers/image/manifest"
 	"github.com/containers/image/signature"
 	"github.com/go-check/check"
-	"github.com/opencontainers/go-digest"
+	digest "github.com/opencontainers/go-digest"
 	imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
 	"github.com/opencontainers/image-tools/image"
 )
@@ -64,7 +64,7 @@ func (s *CopySuite) SetUpSuite(c *check.C) {
 	os.Setenv("GNUPGHOME", s.gpgHome)
 
 	for _, key := range []string{"personal", "official"} {
-		batchInput := fmt.Sprintf("Key-Type: RSA\nName-Real: Test key - %s\nName-email: %s@example.com\n%%commit\n",
+		batchInput := fmt.Sprintf("Key-Type: RSA\nName-Real: Test key - %s\nName-email: %s@example.com\n%%no-protection\n%%commit\n",
 			key, key)
 		runCommandWithInput(c, batchInput, gpgBinary, "--batch", "--gen-key")
 
diff --git a/integration/signing_test.go b/integration/signing_test.go
index 3375d905..8e20f8ff 100644
--- a/integration/signing_test.go
+++ b/integration/signing_test.go
@@ -44,7 +44,7 @@ func (s *SigningSuite) SetUpSuite(c *check.C) {
 	c.Assert(err, check.IsNil)
 	os.Setenv("GNUPGHOME", s.gpgHome)
 
-	runCommandWithInput(c, "Key-Type: RSA\nName-Real: Testing user\n%commit\n", gpgBinary, "--homedir", s.gpgHome, "--batch", "--gen-key")
+	runCommandWithInput(c, "Key-Type: RSA\nName-Real: Testing user\n%no-protection\n%commit\n", gpgBinary, "--homedir", s.gpgHome, "--batch", "--gen-key")
 
 	lines, err := exec.Command(gpgBinary, "--homedir", s.gpgHome, "--with-colons", "--no-permission-warning", "--fingerprint").Output()
 	c.Assert(err, check.IsNil)