pax_global_header00006660000000000000000000000064147202507200014511gustar00rootroot0000000000000052 comment=de26861d2f7c1d0ffeaecc4055edd4bf09c73ece gpgme-0.1.4/000077500000000000000000000000001472025072000126125ustar00rootroot00000000000000gpgme-0.1.4/.github/000077500000000000000000000000001472025072000141525ustar00rootroot00000000000000gpgme-0.1.4/.github/workflows/000077500000000000000000000000001472025072000162075ustar00rootroot00000000000000gpgme-0.1.4/.github/workflows/go.yml000066400000000000000000000017221472025072000173410ustar00rootroot00000000000000name: Test on: push: branches: - master pull_request: branches: - master jobs: ubuntu: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - run: sudo apt-get install -y libgpgme-dev - uses: actions/setup-go@v3 with: go-version: '1.17.12' - run: go test -v ./... ubuntu-ppe64le: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: uraimo/run-on-arch-action@v2 with: arch: ppc64le distro: ubuntu_latest githubToken: ${{ github.token }} install: | apt-get update -q -y apt-get install -y libgpgme-dev golang run: | go test -v ./... macos: runs-on: macos-latest steps: - uses: actions/checkout@v3 - run: brew install gpgme gnupg@1.4 - uses: actions/setup-go@v3 with: go-version: '1.17.12' - run: go test -v ./... gpgme-0.1.4/.gitignore000066400000000000000000000001441472025072000146010ustar00rootroot00000000000000testdata/gpghome/random_seed testdata/gpghome/.gpg-v21-migrated testdata/gpghome/private-keys-v1.d/ gpgme-0.1.4/LICENSE000066400000000000000000000027431472025072000136250ustar00rootroot00000000000000Copyright (c) 2015, James Fargher All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. gpgme-0.1.4/README.md000066400000000000000000000006451472025072000140760ustar00rootroot00000000000000# GPGME (golang) Go wrapper for the GPGME library. This library is intended for use with desktop applications. If you are looking to add OpenPGP support to a server application I suggest you first look at [golang.org/x/crypto/openpgp](https://godoc.org/golang.org/x/crypto/openpgp). ## Installation go get -u github.com/proglottis/gpgme ## Documentation * [godoc](https://godoc.org/github.com/proglottis/gpgme) gpgme-0.1.4/data.go000066400000000000000000000122231472025072000140520ustar00rootroot00000000000000package gpgme // #include // #include // #include // #include "go_gpgme.h" import "C" import ( "io" "os" "runtime" "runtime/cgo" "unsafe" ) const ( SeekSet = C.SEEK_SET SeekCur = C.SEEK_CUR SeekEnd = C.SEEK_END ) var dataCallbacks = C.struct_gpgme_data_cbs{ read: C.gpgme_data_read_cb_t(C.gogpgme_readfunc), write: C.gpgme_data_write_cb_t(C.gogpgme_writefunc), seek: C.gpgme_data_seek_cb_t(C.gogpgme_seekfunc), } //export gogpgme_readfunc func gogpgme_readfunc(handle, buffer unsafe.Pointer, size C.size_t) C.ssize_t { h := *(*cgo.Handle)(handle) d := h.Value().(*Data) n, err := d.r.Read(unsafe.Slice((*byte)(buffer), size)) if err != nil && err != io.EOF { d.err = err C.gpgme_err_set_errno(C.EIO) return -1 } return C.ssize_t(n) } //export gogpgme_writefunc func gogpgme_writefunc(handle, buffer unsafe.Pointer, size C.size_t) C.ssize_t { h := *(*cgo.Handle)(handle) d := h.Value().(*Data) n, err := d.w.Write(unsafe.Slice((*byte)(buffer), size)) if err != nil && err != io.EOF { d.err = err C.gpgme_err_set_errno(C.EIO) return -1 } return C.ssize_t(n) } //export gogpgme_seekfunc func gogpgme_seekfunc(handle unsafe.Pointer, offset C.gpgme_off_t, whence C.int) C.gpgme_off_t { h := *(*cgo.Handle)(handle) d := h.Value().(*Data) n, err := d.s.Seek(int64(offset), int(whence)) if err != nil { d.err = err C.gpgme_err_set_errno(C.EIO) return -1 } return C.gpgme_off_t(n) } // The Data buffer used to communicate with GPGME type Data struct { dh C.gpgme_data_t // WARNING: Call runtime.KeepAlive(d) after ANY passing of d.dh to C r io.Reader w io.Writer s io.Seeker cbc cgo.Handle // WARNING: Call runtime.KeepAlive(d) after ANY use of d.cbc in C (typically via d.dh) err error } func newData() *Data { d := &Data{} runtime.SetFinalizer(d, (*Data).Close) return d } // NewData returns a new memory based data buffer func NewData() (*Data, error) { d := newData() return d, handleError(C.gpgme_data_new(&d.dh)) } // NewDataFile returns a new file based data buffer func NewDataFile(f *os.File) (*Data, error) { d := newData() d.r = f return d, handleError(C.gpgme_data_new_from_fd(&d.dh, C.int(f.Fd()))) } // NewDataBytes returns a new memory based data buffer that contains `b` bytes func NewDataBytes(b []byte) (*Data, error) { d := newData() var cb *C.char if len(b) != 0 { cb = (*C.char)(unsafe.Pointer(&b[0])) } return d, handleError(C.gpgme_data_new_from_mem(&d.dh, cb, C.size_t(len(b)), 1)) } // NewDataReader returns a new callback based data buffer func NewDataReader(r io.Reader) (*Data, error) { d := newData() d.r = r if s, ok := r.(io.Seeker); ok { d.s = s } d.cbc = cgo.NewHandle(d) return d, handleError(C.gpgme_data_new_from_cbs(&d.dh, &dataCallbacks, unsafe.Pointer(&d.cbc))) } // NewDataWriter returns a new callback based data buffer func NewDataWriter(w io.Writer) (*Data, error) { d := newData() d.w = w if s, ok := w.(io.Seeker); ok { d.s = s } d.cbc = cgo.NewHandle(d) return d, handleError(C.gpgme_data_new_from_cbs(&d.dh, &dataCallbacks, unsafe.Pointer(&d.cbc))) } // NewDataReadWriter returns a new callback based data buffer func NewDataReadWriter(rw io.ReadWriter) (*Data, error) { d := newData() d.r = rw d.w = rw if s, ok := rw.(io.Seeker); ok { d.s = s } d.cbc = cgo.NewHandle(d) return d, handleError(C.gpgme_data_new_from_cbs(&d.dh, &dataCallbacks, unsafe.Pointer(&d.cbc))) } // NewDataReadWriteSeeker returns a new callback based data buffer func NewDataReadWriteSeeker(rw io.ReadWriteSeeker) (*Data, error) { d := newData() d.r = rw d.w = rw d.s = rw d.cbc = cgo.NewHandle(d) return d, handleError(C.gpgme_data_new_from_cbs(&d.dh, &dataCallbacks, unsafe.Pointer(&d.cbc))) } // Close releases any resources associated with the data buffer func (d *Data) Close() error { if d.dh == nil { return nil } if d.cbc > 0 { d.cbc.Delete() } _, err := C.gpgme_data_release(d.dh) runtime.KeepAlive(d) d.dh = nil return err } func (d *Data) Write(p []byte) (int, error) { var buffer *byte if len(p) > 0 { buffer = &p[0] } n, err := C.gpgme_data_write(d.dh, unsafe.Pointer(buffer), C.size_t(len(p))) runtime.KeepAlive(d) switch { case d.err != nil: defer func() { d.err = nil }() return 0, d.err case err != nil: return 0, err case len(p) > 0 && n == 0: return 0, io.EOF } return int(n), nil } func (d *Data) Read(p []byte) (int, error) { var buffer *byte if len(p) > 0 { buffer = &p[0] } n, err := C.gpgme_data_read(d.dh, unsafe.Pointer(buffer), C.size_t(len(p))) runtime.KeepAlive(d) switch { case d.err != nil: defer func() { d.err = nil }() return 0, d.err case err != nil: return 0, err case len(p) > 0 && n == 0: return 0, io.EOF } return int(n), nil } func (d *Data) Seek(offset int64, whence int) (int64, error) { n, err := C.gogpgme_data_seek(d.dh, C.gpgme_off_t(offset), C.int(whence)) runtime.KeepAlive(d) switch { case d.err != nil: defer func() { d.err = nil }() return 0, d.err case err != nil: return 0, err } return int64(n), nil } // Name returns the associated filename if any func (d *Data) Name() string { res := C.GoString(C.gpgme_data_get_file_name(d.dh)) runtime.KeepAlive(d) return res } gpgme-0.1.4/data_test.go000066400000000000000000000062111472025072000151110ustar00rootroot00000000000000package gpgme import ( "bytes" "errors" "io" "io/ioutil" "os" "testing" ) func TestData_memory_empty(t *testing.T) { dh, err := NewData() checkError(t, err) for i := 0; i < 5; i++ { _, err := dh.Write([]byte(testCipherText)) checkError(t, err) } _, err = dh.Seek(0, SeekSet) checkError(t, err) testReader(t, dh, bytes.Repeat([]byte(testCipherText), 5)) checkError(t, dh.Close()) } func TestData_memory(t *testing.T) { // Test ordinary data, and empty slices for _, content := range [][]byte{[]byte(testCipherText), []byte{}} { dh, err := NewDataBytes(content) checkError(t, err) testReader(t, dh, content) checkError(t, dh.Close()) } } func TestData_file(t *testing.T) { f, err := ioutil.TempFile("", "gpgme") checkError(t, err) defer func() { checkError(t, f.Close()) checkError(t, os.Remove(f.Name())) }() dh, err := NewDataFile(f) checkError(t, err) for i := 0; i < 5; i++ { _, err := dh.Write([]byte(testCipherText)) checkError(t, err) } _, err = dh.Seek(0, SeekSet) checkError(t, err) testReader(t, dh, bytes.Repeat([]byte(testCipherText), 5)) checkError(t, dh.Close()) } func TestData_callback_reading(t *testing.T) { r := bytes.NewReader([]byte(testCipherText)) dh, err := NewDataReader(r) checkError(t, err) testReader(t, dh, []byte(testCipherText)) checkError(t, dh.Close()) } func TestData_callback_reading_error(t *testing.T) { expectedErr := errors.New("a special error") r := errReadSeeker{err: expectedErr} dh, err := NewDataReader(r) checkError(t, err) _, err = dh.Read(make([]byte, 10)) if err != expectedErr { t.Errorf("err = %v, want %v", err, expectedErr) } checkError(t, dh.Close()) } func TestData_callback_seeking_error(t *testing.T) { expectedErr := errors.New("a special error") r := errReadSeeker{err: expectedErr} dh, err := NewDataReader(r) checkError(t, err) _, err = dh.Seek(0, 0) if err != expectedErr { t.Errorf("err = %v, want %v", err, expectedErr) } checkError(t, dh.Close()) } func TestData_callback_writing(t *testing.T) { var buf bytes.Buffer dh, err := NewDataWriter(&buf) checkError(t, err) for i := 0; i < 5; i++ { _, err := dh.Write([]byte(testCipherText)) checkError(t, err) } expected := bytes.Repeat([]byte(testCipherText), 5) diff(t, buf.Bytes(), expected) checkError(t, dh.Close()) } func TestData_callback_writing_error(t *testing.T) { expectedErr := errors.New("a special error") dh, err := NewDataWriter(errWriter{err: expectedErr}) checkError(t, err) _, err = dh.Write([]byte(testData)) if err != expectedErr { t.Errorf("err = %v, want %v", err, expectedErr) } checkError(t, dh.Close()) } func testReader(t testing.TB, r io.Reader, content []byte) { var buf bytes.Buffer n, err := io.Copy(&buf, r) checkError(t, err) if int(n) != len(content) { t.Errorf("n = %d, want %d", n, len(content)) } diff(t, buf.Bytes(), content) } type errWriter struct{ err error } func (w errWriter) Write(p []byte) (int, error) { return 0, w.err } type errReadSeeker struct{ err error } func (rs errReadSeeker) Read([]byte) (int, error) { return 0, rs.err } func (rs errReadSeeker) Seek(int64, int) (int64, error) { return 0, rs.err } gpgme-0.1.4/go.mod000066400000000000000000000000541472025072000137170ustar00rootroot00000000000000module github.com/proglottis/gpgme go 1.17 gpgme-0.1.4/go_gpgme.c000066400000000000000000000040031472025072000145370ustar00rootroot00000000000000#include "go_gpgme.h" 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, void* data_h, void* inquiry_h, void* status_h, gpgme_error_t *operr ){ return gpgme_op_assuan_transact_ext( ctx, cmd, (gpgme_assuan_data_cb_t) gogpgme_assuan_data_callback, data_h, (gpgme_assuan_inquire_cb_t) gogpgme_assuan_inquiry_callback, inquiry_h, (gpgme_assuan_status_cb_t) gogpgme_assuan_status_callback, status_h, operr ); } unsigned int key_revoked(gpgme_key_t k) { return k->revoked; } unsigned int key_expired(gpgme_key_t k) { return k->expired; } unsigned int key_disabled(gpgme_key_t k) { return k->disabled; } unsigned int key_invalid(gpgme_key_t k) { return k->invalid; } unsigned int key_can_encrypt(gpgme_key_t k) { return k->can_encrypt; } unsigned int key_can_sign(gpgme_key_t k) { return k->can_sign; } unsigned int key_can_certify(gpgme_key_t k) { return k->can_certify; } unsigned int key_secret(gpgme_key_t k) { return k->secret; } unsigned int key_can_authenticate(gpgme_key_t k) { return k->can_authenticate; } unsigned int key_is_qualified(gpgme_key_t k) { return k->is_qualified; } unsigned int signature_wrong_key_usage(gpgme_signature_t s) { return s->wrong_key_usage; } unsigned int signature_pka_trust(gpgme_signature_t s) { return s->pka_trust; } unsigned int signature_chain_model(gpgme_signature_t s) { return s->chain_model; } unsigned int subkey_revoked(gpgme_subkey_t k) { return k->revoked; } unsigned int subkey_expired(gpgme_subkey_t k) { return k->expired; } unsigned int subkey_disabled(gpgme_subkey_t k) { return k->disabled; } unsigned int subkey_invalid(gpgme_subkey_t k) { return k->invalid; } unsigned int subkey_secret(gpgme_subkey_t k) { return k->secret; } unsigned int uid_revoked(gpgme_user_id_t u) { return u->revoked; } unsigned int uid_invalid(gpgme_user_id_t u) { return u->invalid; } gpgme-0.1.4/go_gpgme.h000066400000000000000000000037741472025072000145620ustar00rootroot00000000000000#ifndef GO_GPGME_H #define GO_GPGME_H #define _FILE_OFFSET_BITS 64 #include #include 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_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, void *data_h, void *inquiry_h , void *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); extern unsigned int key_disabled(gpgme_key_t k); extern unsigned int key_invalid(gpgme_key_t k); extern unsigned int key_can_encrypt(gpgme_key_t k); extern unsigned int key_can_sign(gpgme_key_t k); extern unsigned int key_can_certify(gpgme_key_t k); extern unsigned int key_secret(gpgme_key_t k); extern unsigned int key_can_authenticate(gpgme_key_t k); extern unsigned int key_is_qualified(gpgme_key_t k); extern unsigned int signature_wrong_key_usage(gpgme_signature_t s); extern unsigned int signature_pka_trust(gpgme_signature_t s); extern unsigned int signature_chain_model(gpgme_signature_t s); extern unsigned int subkey_revoked(gpgme_subkey_t k); extern unsigned int subkey_expired(gpgme_subkey_t k); extern unsigned int subkey_disabled(gpgme_subkey_t k); extern unsigned int subkey_invalid(gpgme_subkey_t k); extern unsigned int subkey_secret(gpgme_subkey_t k); extern unsigned int uid_revoked(gpgme_user_id_t u); extern unsigned int uid_invalid(gpgme_user_id_t u); #endif gpgme-0.1.4/gpgme.go000066400000000000000000000562361472025072000142540ustar00rootroot00000000000000// Package gpgme provides a Go wrapper for the GPGME library package gpgme // #cgo pkg-config: gpgme // #cgo CPPFLAGS: -D_FILE_OFFSET_BITS=64 // #include // #include // #include "go_gpgme.h" import "C" import ( "fmt" "io" "os" "runtime" "runtime/cgo" "time" "unsafe" ) var Version string func init() { Version = C.GoString(C.gpgme_check_version(nil)) } // Callback is the function that is called when a passphrase is required type Callback func(uidHint string, prevWasBad bool, f *os.File) error //export gogpgme_passfunc func gogpgme_passfunc(hook unsafe.Pointer, uid_hint, passphrase_info *C.char, prev_was_bad, fd C.int) C.gpgme_error_t { h := *(*cgo.Handle)(hook) c := h.Value().(*Context) go_uid_hint := C.GoString(uid_hint) f := os.NewFile(uintptr(fd), go_uid_hint) defer f.Close() err := c.callback(go_uid_hint, prev_was_bad != 0, f) if err != nil { return C.GPG_ERR_CANCELED } return 0 } type Protocol int const ( ProtocolOpenPGP Protocol = C.GPGME_PROTOCOL_OpenPGP ProtocolCMS Protocol = C.GPGME_PROTOCOL_CMS ProtocolGPGConf Protocol = C.GPGME_PROTOCOL_GPGCONF ProtocolAssuan Protocol = C.GPGME_PROTOCOL_ASSUAN ProtocolG13 Protocol = C.GPGME_PROTOCOL_G13 ProtocolUIServer Protocol = C.GPGME_PROTOCOL_UISERVER ProtocolDefault Protocol = C.GPGME_PROTOCOL_DEFAULT ProtocolUnknown Protocol = C.GPGME_PROTOCOL_UNKNOWN ) type PinEntryMode int const ( PinEntryDefault PinEntryMode = C.GPGME_PINENTRY_MODE_DEFAULT PinEntryAsk PinEntryMode = C.GPGME_PINENTRY_MODE_ASK PinEntryCancel PinEntryMode = C.GPGME_PINENTRY_MODE_CANCEL PinEntryError PinEntryMode = C.GPGME_PINENTRY_MODE_ERROR PinEntryLoopback PinEntryMode = C.GPGME_PINENTRY_MODE_LOOPBACK ) type EncryptFlag uint const ( EncryptAlwaysTrust EncryptFlag = C.GPGME_ENCRYPT_ALWAYS_TRUST EncryptNoEncryptTo EncryptFlag = C.GPGME_ENCRYPT_NO_ENCRYPT_TO EncryptPrepare EncryptFlag = C.GPGME_ENCRYPT_PREPARE EncryptExceptSign EncryptFlag = C.GPGME_ENCRYPT_EXPECT_SIGN ) type HashAlgo int // const values for HashAlgo values should be added when necessary. type KeyListMode uint const ( KeyListModeLocal KeyListMode = C.GPGME_KEYLIST_MODE_LOCAL KeyListModeExtern KeyListMode = C.GPGME_KEYLIST_MODE_EXTERN KeyListModeSigs KeyListMode = C.GPGME_KEYLIST_MODE_SIGS KeyListModeSigNotations KeyListMode = C.GPGME_KEYLIST_MODE_SIG_NOTATIONS KeyListModeEphemeral KeyListMode = C.GPGME_KEYLIST_MODE_EPHEMERAL KeyListModeModeValidate KeyListMode = C.GPGME_KEYLIST_MODE_VALIDATE ) type PubkeyAlgo int // const values for PubkeyAlgo values should be added when necessary. type SigMode int const ( SigModeNormal SigMode = C.GPGME_SIG_MODE_NORMAL SigModeDetach SigMode = C.GPGME_SIG_MODE_DETACH SigModeClear SigMode = C.GPGME_SIG_MODE_CLEAR ) type SigSum int const ( SigSumValid SigSum = C.GPGME_SIGSUM_VALID SigSumGreen SigSum = C.GPGME_SIGSUM_GREEN SigSumRed SigSum = C.GPGME_SIGSUM_RED SigSumKeyRevoked SigSum = C.GPGME_SIGSUM_KEY_REVOKED SigSumKeyExpired SigSum = C.GPGME_SIGSUM_KEY_EXPIRED SigSumSigExpired SigSum = C.GPGME_SIGSUM_SIG_EXPIRED SigSumKeyMissing SigSum = C.GPGME_SIGSUM_KEY_MISSING SigSumCRLMissing SigSum = C.GPGME_SIGSUM_CRL_MISSING SigSumCRLTooOld SigSum = C.GPGME_SIGSUM_CRL_TOO_OLD SigSumBadPolicy SigSum = C.GPGME_SIGSUM_BAD_POLICY SigSumSysError SigSum = C.GPGME_SIGSUM_SYS_ERROR ) type Validity int const ( ValidityUnknown Validity = C.GPGME_VALIDITY_UNKNOWN ValidityUndefined Validity = C.GPGME_VALIDITY_UNDEFINED ValidityNever Validity = C.GPGME_VALIDITY_NEVER ValidityMarginal Validity = C.GPGME_VALIDITY_MARGINAL ValidityFull Validity = C.GPGME_VALIDITY_FULL ValidityUltimate Validity = C.GPGME_VALIDITY_ULTIMATE ) type ErrorCode int const ( ErrorNoError ErrorCode = C.GPG_ERR_NO_ERROR ErrorEOF ErrorCode = C.GPG_ERR_EOF ) // Error is a wrapper for GPGME errors type Error struct { err C.gpgme_error_t } func (e Error) Code() ErrorCode { return ErrorCode(C.gpgme_err_code(e.err)) } func (e Error) Error() string { return C.GoString(C.gpgme_strerror(e.err)) } func handleError(err C.gpgme_error_t) error { e := Error{err: err} if e.Code() == ErrorNoError { return nil } return e } func cbool(b bool) C.int { if b { return 1 } return 0 } func EngineCheckVersion(p Protocol) error { return handleError(C.gpgme_engine_check_version(C.gpgme_protocol_t(p))) } type EngineInfo struct { 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 { return e.next } func (e *EngineInfo) Protocol() Protocol { return e.protocol } func (e *EngineInfo) FileName() string { return e.fileName } func (e *EngineInfo) Version() string { return e.version } func (e *EngineInfo) RequiredVersion() string { return e.requiredVersion } func (e *EngineInfo) HomeDir() string { return e.homeDir } func GetEngineInfo() (*EngineInfo, error) { 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 { var cfn, chome *C.char if fileName != "" { cfn = C.CString(fileName) defer C.free(unsafe.Pointer(cfn)) } if homeDir != "" { chome = C.CString(homeDir) defer C.free(unsafe.Pointer(chome)) } return handleError(C.gpgme_set_engine_info(C.gpgme_protocol_t(proto), cfn, chome)) } func GetDirInfo(what string) string { cwhat := C.CString(what) defer C.free(unsafe.Pointer(cwhat)) cdir := C.gpgme_get_dirinfo(cwhat) if cdir == nil { return "" } return C.GoString(cdir) } func FindKeys(pattern string, secretOnly bool) ([]*Key, error) { var keys []*Key ctx, err := New() if err != nil { return keys, err } defer ctx.Release() if err := ctx.KeyListStart(pattern, secretOnly); err != nil { return keys, err } defer func() { _ = ctx.KeyListEnd() }() for ctx.KeyListNext() { keys = append(keys, ctx.Key) } if ctx.KeyError != nil { return keys, ctx.KeyError } return keys, nil } func Decrypt(r io.Reader) (*Data, error) { ctx, err := New() if err != nil { return nil, err } defer ctx.Release() cipher, err := NewDataReader(r) if err != nil { return nil, err } defer cipher.Close() plain, err := NewData() if err != nil { return nil, err } if err := ctx.Decrypt(cipher, plain); err != nil { return nil, err } _, err = plain.Seek(0, SeekSet) return plain, err } type Context struct { Key *Key KeyError error callback Callback cbc cgo.Handle // WARNING: Call runtime.KeepAlive(c) after ANY use of c.cbc in C (typically via c.ctx) ctx C.gpgme_ctx_t // WARNING: Call runtime.KeepAlive(c) after ANY passing of c.ctx to C } func New() (*Context, error) { c := &Context{} err := C.gpgme_new(&c.ctx) runtime.SetFinalizer(c, (*Context).Release) return c, handleError(err) } func (c *Context) Release() { if c.ctx == nil { return } if c.cbc > 0 { c.cbc.Delete() } 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 { 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 { res := C.gpgme_get_textmode(c.ctx) != 0 runtime.KeepAlive(c) return res } func (c *Context) SetProtocol(p Protocol) error { err := handleError(C.gpgme_set_protocol(c.ctx, C.gpgme_protocol_t(p))) runtime.KeepAlive(c) return err } func (c *Context) Protocol() Protocol { res := Protocol(C.gpgme_get_protocol(c.ctx)) runtime.KeepAlive(c) return res } func (c *Context) SetKeyListMode(m KeyListMode) error { 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 { res := KeyListMode(C.gpgme_get_keylist_mode(c.ctx)) runtime.KeepAlive(c) return res } func (c *Context) SetPinEntryMode(m PinEntryMode) error { err := handleError(C.gpgme_set_pinentry_mode(c.ctx, C.gpgme_pinentry_mode_t(m))) runtime.KeepAlive(c) return err } func (c *Context) PinEntryMode() PinEntryMode { res := PinEntryMode(C.gpgme_get_pinentry_mode(c.ctx)) runtime.KeepAlive(c) return res } func (c *Context) SetCallback(callback Callback) error { var err error c.callback = callback if c.cbc > 0 { c.cbc.Delete() } if callback != nil { c.cbc = cgo.NewHandle(c) _, err = C.gpgme_set_passphrase_cb(c.ctx, C.gpgme_passphrase_cb_t(C.gogpgme_passfunc), unsafe.Pointer(&c.cbc)) } else { c.cbc = 0 _, err = C.gpgme_set_passphrase_cb(c.ctx, nil, nil) } runtime.KeepAlive(c) return err } func (c *Context) EngineInfo() *EngineInfo { 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 { var cfn, chome *C.char if fileName != "" { cfn = C.CString(fileName) defer C.free(unsafe.Pointer(cfn)) } if homeDir != "" { chome = C.CString(homeDir) defer C.free(unsafe.Pointer(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 := 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 } else { c.KeyError = err } return false } c.KeyError = nil return true } func (c *Context) KeyListEnd() error { err := handleError(C.gpgme_op_keylist_end(c.ctx)) runtime.KeepAlive(c) return err } func (c *Context) GetKey(fingerprint string, secret bool) (*Key, error) { key := newKey() cfpr := C.CString(fingerprint) defer C.free(unsafe.Pointer(cfpr)) err := handleError(C.gpgme_get_key(c.ctx, cfpr, &key.k, cbool(secret))) 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 { return nil, err } return key, nil } func (c *Context) Decrypt(ciphertext, plaintext *Data) error { 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 { 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 { Summary SigSum Fingerprint string Status error Timestamp time.Time ExpTimestamp time.Time WrongKeyUsage bool PKATrust uint ChainModel bool Validity Validity ValidityReason error PubkeyAlgo PubkeyAlgo HashAlgo HashAlgo } func (c *Context) Verify(sig, signedText, plain *Data) (string, []Signature, error) { var signedTextPtr, plainPtr C.gpgme_data_t = nil, nil if signedText != nil { signedTextPtr = signedText.dh } if plain != nil { 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{ Summary: SigSum(s.summary), Fingerprint: C.GoString(s.fpr), Status: handleError(s.status), // s.notations not implemented Timestamp: time.Unix(int64(s.timestamp), 0), ExpTimestamp: time.Unix(int64(s.exp_timestamp), 0), WrongKeyUsage: C.signature_wrong_key_usage(s) != 0, PKATrust: uint(C.signature_pka_trust(s)), ChainModel: C.signature_chain_model(s) != 0, Validity: Validity(s.validity), ValidityReason: handleError(s.validity_reason), PubkeyAlgo: PubkeyAlgo(s.pubkey_algo), HashAlgo: HashAlgo(s.hash_algo), } sigs = append(sigs, sig) } 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 { size := unsafe.Sizeof(new(C.gpgme_key_t)) recp := C.calloc(C.size_t(len(recipients)+1), C.size_t(size)) defer C.free(recp) for i := range recipients { ptr := (*C.gpgme_key_t)(unsafe.Pointer(uintptr(recp) + size*uintptr(i))) *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 { 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 } } 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 AssuanInquireCallback func(name, args string) error 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 := cgo.NewHandle(&data) inquiryPtr := cgo.NewHandle(&inquiry) statusPtr := cgo.NewHandle(&status) cmdCStr := C.CString(cmd) defer C.free(unsafe.Pointer(cmdCStr)) err := C.gogpgme_op_assuan_transact_ext( c.ctx, cmdCStr, unsafe.Pointer(&dataPtr), unsafe.Pointer(&inquiryPtr), unsafe.Pointer(&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 { h := *(*cgo.Handle)(handle) c := h.Value().(*AssuanDataCallback) if *c == nil { return 0 } if err := (*c)(C.GoBytes(data, C.int(datalen))); err != nil { return C.gpgme_error(C.GPG_ERR_USER_1) } 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) h := *(*cgo.Handle)(handle) c := h.Value().(*AssuanInquireCallback) if *c == nil { return 0 } if err := (*c)(name, args); err != nil { return C.gpgme_error(C.GPG_ERR_USER_1) } 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) h := *(*cgo.Handle)(handle) c := h.Value().(*AssuanStatusCallback) if *c == nil { return 0 } if err := (*c)(status, args); err != nil { return C.gpgme_error(C.GPG_ERR_USER_1) } 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". type ImportStatusFlags uint const ( ImportNew ImportStatusFlags = C.GPGME_IMPORT_NEW ImportUID ImportStatusFlags = C.GPGME_IMPORT_UID ImportSIG ImportStatusFlags = C.GPGME_IMPORT_SIG ImportSubKey ImportStatusFlags = C.GPGME_IMPORT_SUBKEY ImportSecret ImportStatusFlags = C.GPGME_IMPORT_SECRET ) type ImportStatus struct { Fingerprint string Result error Status ImportStatusFlags } type ImportResult struct { Considered int NoUserID int Imported int ImportedRSA int Unchanged int NewUserIDs int NewSubKeys int NewSignatures int NewRevocations int SecretRead int SecretImported int SecretUnchanged int NotImported int Imports []ImportStatus } 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{ Fingerprint: C.GoString(s.fpr), Result: handleError(s.result), Status: ImportStatusFlags(s.status), }) } importResult := &ImportResult{ Considered: int(res.considered), NoUserID: int(res.no_user_id), Imported: int(res.imported), ImportedRSA: int(res.imported_rsa), Unchanged: int(res.unchanged), NewUserIDs: int(res.new_user_ids), NewSubKeys: int(res.new_sub_keys), NewSignatures: int(res.new_signatures), NewRevocations: int(res.new_revocations), SecretRead: int(res.secret_read), SecretImported: int(res.secret_imported), SecretUnchanged: int(res.secret_unchanged), NotImported: int(res.not_imported), Imports: imports, } runtime.KeepAlive(c) // for all accesses to res above return importResult, nil } type Key struct { k C.gpgme_key_t // WARNING: Call Runtime.KeepAlive(k) after ANY passing of k.k to C } func newKey() *Key { k := &Key{} runtime.SetFinalizer(k, (*Key).Release) return k } func (k *Key) Release() { C.gpgme_key_release(k.k) runtime.KeepAlive(k) k.k = nil } func (k *Key) Revoked() bool { res := C.key_revoked(k.k) != 0 runtime.KeepAlive(k) return res } func (k *Key) Expired() bool { res := C.key_expired(k.k) != 0 runtime.KeepAlive(k) return res } func (k *Key) Disabled() bool { res := C.key_disabled(k.k) != 0 runtime.KeepAlive(k) return res } func (k *Key) Invalid() bool { res := C.key_invalid(k.k) != 0 runtime.KeepAlive(k) return res } func (k *Key) CanEncrypt() bool { res := C.key_can_encrypt(k.k) != 0 runtime.KeepAlive(k) return res } func (k *Key) CanSign() bool { res := C.key_can_sign(k.k) != 0 runtime.KeepAlive(k) return res } func (k *Key) CanCertify() bool { res := C.key_can_certify(k.k) != 0 runtime.KeepAlive(k) return res } func (k *Key) Secret() bool { res := C.key_secret(k.k) != 0 runtime.KeepAlive(k) return res } func (k *Key) CanAuthenticate() bool { res := C.key_can_authenticate(k.k) != 0 runtime.KeepAlive(k) return res } func (k *Key) IsQualified() bool { res := C.key_is_qualified(k.k) != 0 runtime.KeepAlive(k) return res } func (k *Key) Protocol() Protocol { res := Protocol(k.k.protocol) runtime.KeepAlive(k) return res } func (k *Key) IssuerSerial() string { res := C.GoString(k.k.issuer_serial) runtime.KeepAlive(k) return res } func (k *Key) IssuerName() string { res := C.GoString(k.k.issuer_name) runtime.KeepAlive(k) return res } func (k *Key) ChainID() string { res := C.GoString(k.k.chain_id) runtime.KeepAlive(k) return res } func (k *Key) OwnerTrust() Validity { res := Validity(k.k.owner_trust) runtime.KeepAlive(k) return res } func (k *Key) SubKeys() *SubKey { subKeys := k.k.subkeys runtime.KeepAlive(k) if subKeys == nil { return nil } return &SubKey{k: subKeys, parent: k} // The parent: k reference ensures subKeys remains valid } func (k *Key) UserIDs() *UserID { uids := k.k.uids runtime.KeepAlive(k) if uids == nil { return nil } return &UserID{u: uids, parent: k} // The parent: k reference ensures uids remains valid } func (k *Key) KeyListMode() KeyListMode { res := KeyListMode(k.k.keylist_mode) runtime.KeepAlive(k) return res } type SubKey struct { k C.gpgme_subkey_t parent *Key // make sure the key is not released when we have a reference to a subkey } func (k *SubKey) Next() *SubKey { if k.k.next == nil { return nil } return &SubKey{k: k.k.next, parent: k.parent} } func (k *SubKey) Revoked() bool { return C.subkey_revoked(k.k) != 0 } func (k *SubKey) Expired() bool { return C.subkey_expired(k.k) != 0 } func (k *SubKey) Disabled() bool { return C.subkey_disabled(k.k) != 0 } func (k *SubKey) Invalid() bool { return C.subkey_invalid(k.k) != 0 } func (k *SubKey) Secret() bool { return C.subkey_secret(k.k) != 0 } func (k *SubKey) KeyID() string { return C.GoString(k.k.keyid) } func (k *SubKey) Fingerprint() string { return C.GoString(k.k.fpr) } func (k *SubKey) Created() time.Time { if k.k.timestamp <= 0 { return time.Time{} } return time.Unix(int64(k.k.timestamp), 0) } func (k *SubKey) Expires() time.Time { if k.k.expires <= 0 { return time.Time{} } return time.Unix(int64(k.k.expires), 0) } func (k *SubKey) CardNumber() string { return C.GoString(k.k.card_number) } type UserID struct { u C.gpgme_user_id_t parent *Key // make sure the key is not released when we have a reference to a user ID } func (u *UserID) Next() *UserID { if u.u.next == nil { return nil } return &UserID{u: u.u.next, parent: u.parent} } func (u *UserID) Revoked() bool { return C.uid_revoked(u.u) != 0 } func (u *UserID) Invalid() bool { return C.uid_invalid(u.u) != 0 } func (u *UserID) Validity() Validity { return Validity(u.u.validity) } func (u *UserID) UID() string { return C.GoString(u.u.uid) } func (u *UserID) Name() string { return C.GoString(u.u.name) } func (u *UserID) Comment() string { return C.GoString(u.u.comment) } func (u *UserID) Email() string { return C.GoString(u.u.email) } gpgme-0.1.4/gpgme_test.go000066400000000000000000000275701472025072000153120ustar00rootroot00000000000000package gpgme import ( "bytes" "flag" "io" "io/ioutil" "os" "os/exec" "path/filepath" "strings" "testing" ) const ( testGPGHome = "./testdata/gpghome" testData = "data\n" testCipherText = `-----BEGIN PGP MESSAGE----- Version: GnuPG v1 hQEMAw4698hF4WUhAQf/SdkbF/zUE6YjBxscDurrUZunSnt87kipLXypSxTDIdgj O9huAaQwBz4uAJf2DuEN/7iAFGhi/v45NTujrG+7ocfjM3m/A2T80g4RVF5kKXBr pFFgH7bMRY6VdZt1GKI9izSO/uFkoKXG8M31tCX3hWntQUJ9p+n1avGpu3wo4Ru3 CJhpL+ChDzXuZv4IK81ahrixEz4fJH0vd0TbsHpTXx4WPkTGXelM0R9PwiY7TovZ onGZUIplrfA1HUUbQfzExyFw3oAo1/almzD5CBIfq5EnU8Siy5BNulDXm0/44h8A lOUy6xqx7ITnWMYYf4a1cFoW80Yk+x6SYQqbcqHFQdJIAVr00V4pPV4ppFcXgdw/ BxKERzDupyqS0tcfVFCYLRmvtQp7ceOS6jRW3geXPPIz1U/VYBvKlvFu4XTMCS6z 4qY4SzZlFEsU =IQOA -----END PGP MESSAGE-----` textSignedCipherText = `-----BEGIN PGP MESSAGE----- Version: GnuPG v1 hQEMAw4698hF4WUhAQf9FaI8zSltIMcDxqGtpcTfcBtC30rcKqE/oa1bacxuZQKW uT671N7J2TjU8tLIsAO9lDSRHCsf6DbB+O6qOJsu6eJownyMUEmJ2oBON9Z2x+Ci aS0KeRqcRxNJYbIth9/ffa2I4sSBA0GS93yCfGKHNL4JvNo/dgVjJwOPWZ5TKu49 n9h81LMwTQ8qpLGPeo7nsgBtSKNTi5s7pC0bd9HTii7gQcLUEeeqk5U7oG5CRN48 zKPrVud8pIDvSoYuymZmjWI1MmF4xQ+6IHK7hWKMyA2RkS4CpyBPERMifc+y+K2+ LnKLt2ul9jxgqv3fd/dU7UhyqdvJdj83tUvhN4Iru9LAuQHfVsP23keH8CHpUzCj PkuUYvpgmRdMSC+l3yBCqnaaAI51rRdyJ+HX4DrrinOFst2JfXEICiQAg5UkO4i/ YYHsfD3xfk7MKMOY/DfV25OyAU6Yeq/5SMisONhtlzcosVU7HULudbIKSG6q/tCx jRGu+oMIBdiQ+eipnaLVetyGxSEDzxHH367NbxC9ffxK7I+Srmwh+oS/yMNuZA8r bUp6NbykK8Si7VNgVG1+yOIULLuR0VBO5+e2PKQU8FP0wABTn7h3zE8z4rCrcaZl 43bc/TBUyTdWY79e2P1otaCbhQGAjfF8ClPbAdsFppykf5I2ZDDf8c67d0Nqui74 Yry4/fF/2sAzyoyzdP6ktBw6pCA1MbwDqTGg7aEI8Es3X+jfh3qIaLnGmZ7jIyUl D4jgGAEpFU+mpPH8eukKuT+OJ3P6gdmSiAJH7+C96tlcEg9BNxjYNkCTil/3yygQ EV/5zFTEpzm4CtYHHdmY5uCaEJq/4hhE8BY8 =8mxI -----END PGP MESSAGE-----` testSignedText = `-----BEGIN PGP MESSAGE----- Version: GnuPG v1 owEBQwG8/pANAwACAQMn/7Ain2E2AcsTYgBW47+PVGVzdCBtZXNzYWdlCokBHAQA AQIABgUCVuO/jwAKCRADJ/+wIp9hNh9lCACPiDkY0CqI9ss4EBcpToqnF/8NmV99 2wi6FmbQnUmY98OMM2VJXrX6PvfD/X+FsiLog0CZU4heMEAI3Dd3qELgTfaTFqNc bbDkenzA0kO734WLsEU/z1F9iWAcfeF3crKqd3fBw5kZ1PkhuJFdcqQEOUQALvXY 8VAtQmQWzf3sn2KIQ7R1wyAJVoUZaN5Xwc9Y0F1l4Xxifax8nkFBl35X6gmHRxZ7 jlfmWMcAkXASNXl9/Yso2XGJMs85JPhZPJ3KJRuuurnhZSxAbDJMNBFJ+HbQv3y6 pupeR7ut6pWJxr6MND793yoFGoRYwKklQdfP4xzFCatYRU4RkPBp95KJ =RMUj -----END PGP MESSAGE----- ` ) func TestMain(m *testing.M) { flag.Parse() os.Setenv("GNUPGHOME", absTestGPGHome()) unsetenvGPGAgentInfo() os.Exit(m.Run()) } func compareEngineInfo(t *testing.T, info *EngineInfo, proto Protocol, fileName, homeDir string) { for info != nil && info.Protocol() != proto { info = info.Next() } if info == nil { t.Errorf("Expected engine info %d not found", proto) return } if info.FileName() != fileName { t.Errorf("Testing file name %s does not match %s", info.FileName(), fileName) } if info.HomeDir() != homeDir { t.Errorf("Testing home directory %s does not match %s", info.HomeDir(), homeDir) } } func TestEngineInfo(t *testing.T) { testProto := ProtocolOpenPGP // Careful, this is global state! defer func() { _ = SetEngineInfo(testProto, "", "") // Try to reset to defaults after we are done. }() testFN := "testFN" testHomeDir := "testHomeDir" checkError(t, SetEngineInfo(testProto, testFN, testHomeDir)) info, err := GetEngineInfo() checkError(t, err) compareEngineInfo(t, info, testProto, testFN, testHomeDir) // SetEngineInfo with empty strings works, using defaults which we don't know, // so just test that it doesn't fail. checkError(t, SetEngineInfo(testProto, testFN, "")) checkError(t, SetEngineInfo(testProto, "", testHomeDir)) } func TestGetDirInfo(t *testing.T) { info := GetDirInfo("fail") if info != "" { t.Errorf("expected no info, got: %q", info) } info = GetDirInfo("homedir") if info == "" { t.Error("expected dir info, got nothing") } } func ctxWithCallback(t *testing.T) *Context { ensureVersion(t, "1.", "can only set password callback for GPG v1.x") ctx, err := New() checkError(t, err) checkError(t, ctx.SetCallback(func(uid_hint string, prev_was_bad bool, f *os.File) error { if prev_was_bad { t.Fatal("Bad passphrase") } _, err := io.WriteString(f, "password\n") return err })) return ctx } func TestContext_Armor(t *testing.T) { ctx, err := New() checkError(t, err) ctx.SetArmor(true) if !ctx.Armor() { t.Error("expected armor set") } ctx.SetArmor(false) if ctx.Armor() { t.Error("expected armor not set") } } func TestContext_TextMode(t *testing.T) { ctx, err := New() checkError(t, err) ctx.SetTextMode(true) if !ctx.TextMode() { t.Error("expected textmode set") } ctx.SetTextMode(false) if ctx.TextMode() { t.Error("expected textmode not set") } } func TestContext_EngineInfo(t *testing.T) { ctx, err := New() checkError(t, err) testProto := ProtocolOpenPGP testFN := "testFN" testHomeDir := "testHomeDir" checkError(t, ctx.SetEngineInfo(testProto, testFN, testHomeDir)) info := ctx.EngineInfo() compareEngineInfo(t, info, testProto, testFN, testHomeDir) // SetEngineInfo with empty strings works, using defaults which we don't know, // so just test that it doesn't fail. checkError(t, ctx.SetEngineInfo(testProto, testFN, "")) checkError(t, ctx.SetEngineInfo(testProto, "", testHomeDir)) } func TestContext_Encrypt(t *testing.T) { ctx, err := New() checkError(t, err) keys, err := FindKeys("test@example.com", true) checkError(t, err) plain, err := NewDataBytes([]byte(testData)) checkError(t, err) var buf bytes.Buffer cipher, err := NewDataWriter(&buf) checkError(t, err) checkError(t, ctx.Encrypt(keys, 0, plain, cipher)) if buf.Len() < 1 { t.Error("Expected encrypted bytes, got empty buffer") } } func TestContext_Decrypt(t *testing.T) { ctx := ctxWithCallback(t) cipher, err := NewDataBytes([]byte(testCipherText)) checkError(t, err) var buf bytes.Buffer plain, err := NewDataWriter(&buf) checkError(t, err) checkError(t, ctx.Decrypt(cipher, plain)) diff(t, buf.Bytes(), []byte("Test message\n")) } func TestContext_DecryptVerify(t *testing.T) { ctx := ctxWithCallback(t) cipher, err := NewDataBytes([]byte(textSignedCipherText)) checkError(t, err) var buf bytes.Buffer plain, err := NewDataWriter(&buf) checkError(t, err) checkError(t, ctx.DecryptVerify(cipher, plain)) diff(t, buf.Bytes(), []byte("Test message\n")) } func TestContext_Sign(t *testing.T) { ctx := ctxWithCallback(t) key, err := ctx.GetKey("test@example.com", true) checkError(t, err) plain, err := NewDataBytes([]byte(testData)) checkError(t, err) var buf bytes.Buffer signed, err := NewDataWriter(&buf) checkError(t, err) checkError(t, ctx.Sign([]*Key{key}, plain, signed, SigModeNormal)) if buf.Len() < 1 { t.Error("Expected signed bytes, got empty buffer") } } func TestContext_Verify(t *testing.T) { ctx, err := New() checkError(t, err) _, err = ctx.GetKey("test@example.com", false) checkError(t, err) signed, err := NewDataBytes([]byte(testSignedText)) checkError(t, err) var buf bytes.Buffer plain, err := NewDataWriter(&buf) checkError(t, err) _, sigs, err := ctx.Verify(signed, nil, plain) checkError(t, err) if len(sigs) != 1 { t.Error("Expected 1 signature") } sig := sigs[0] // Normalize expectedSig := Signature{ Summary: SigSumValid | SigSumGreen, Fingerprint: "44B646DC347C31E867FF4F450327FFB0229F6136", Status: nil, Timestamp: sig.Timestamp, // Ignore in comparison ExpTimestamp: sig.ExpTimestamp, // Ignore in comparison WrongKeyUsage: false, PKATrust: 0, ChainModel: false, Validity: ValidityFull, ValidityReason: nil, PubkeyAlgo: sig.PubkeyAlgo, // Ignore in comparison HashAlgo: sig.HashAlgo, // Ignore in comparison } if sig != expectedSig { t.Errorf("Signature verification does not match: %#v vs. %#v", sig, expectedSig) } diff(t, buf.Bytes(), []byte("Test message\n")) } func TestContext_Import(t *testing.T) { homeDir, err := ioutil.TempDir("", "gpgme-import-test") checkError(t, err) defer os.RemoveAll(homeDir) ctx, err := New() checkError(t, err) checkError(t, ctx.SetEngineInfo(ProtocolOpenPGP, "", homeDir)) f, err := os.Open("./testdata/pubkeys.gpg") checkError(t, err) defer f.Close() dh, err := NewDataFile(f) checkError(t, err) defer dh.Close() res, err := ctx.Import(dh) checkError(t, err) for _, v := range []struct { Name string Value int Expected int }{ {"Considered", res.Considered, 1}, {"NoUserID", res.NoUserID, 0}, {"Imported", res.Imported, 1}, // Do not test ImportedRSA, as of gnupg 2.1.11 the value is always 0. {"Unchanged", res.Unchanged, 0}, {"NewUserIDs", res.NewUserIDs, 0}, {"NewSubKeys", res.NewSubKeys, 0}, {"NewSignatures", res.NewSignatures, 0}, {"NewRevocations", res.NewRevocations, 0}, {"SecretRead", res.SecretRead, 0}, {"SecretImported", res.SecretImported, 0}, {"SecretUnchanged", res.SecretUnchanged, 0}, {"NotImported", res.NotImported, 0}, } { if v.Value != v.Expected { t.Errorf("Unexpected import result field %s, value %d, expected %d", v.Name, v.Value, v.Expected) } } expectedStatus := ImportStatus{ Fingerprint: "44B646DC347C31E867FF4F450327FFB0229F6136", Result: nil, Status: ImportNew, } if len(res.Imports) != 1 { t.Errorf("Unexpected number of import status values %d", len(res.Imports)) } else if res.Imports[0] != expectedStatus { t.Errorf("Import status does not match: %#v vs. %#v", res.Imports[0], expectedStatus) } } func TestContext_Export(t *testing.T) { ctx, err := New() checkError(t, err) data, err := NewData() checkError(t, err) err = ctx.Export("", 0, data) checkError(t, err) _, _ = data.Seek(0, 0) allKeys, err := ioutil.ReadAll(data) checkError(t, err) if len(allKeys) < 1 { t.Error("Expected exported keys, got empty buffer") } } func TestContext_AssuanSend(t *testing.T) { // Launch a gpg-agent in daemon mode cmd := exec.Command("gpg-connect-agent", "--verbose", "/bye") cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr err := cmd.Run() if err != nil { t.Skip(err) } ctx, err := New() checkError(t, err) err = ctx.SetProtocol(ProtocolAssuan) checkError(t, err) // NOP should simply succeed err = ctx.AssuanSend("NOP", nil, nil, nil) checkError(t, err) // GETINFO should return a version in Data var versionData []byte err = ctx.AssuanSend("GETINFO version", func(data []byte) error { versionData = data return nil }, nil, nil, ) checkError(t, err) if len(versionData) == 0 { t.Error("Expected version data") } err = ctx.AssuanSend("KILLAGENT", nil, nil, nil) checkError(t, err) } func isVersion(t testing.TB, version string) bool { t.Helper() var info *EngineInfo info, err := GetEngineInfo() checkError(t, err) for info != nil { if info.Protocol() == ProtocolOpenPGP { if strings.Contains(info.FileName(), "gpg") && strings.HasPrefix(info.Version(), version) { return true } return false } info = info.Next() } return false } var gpgBins = []string{"gpg2", "gpg1", "gpg"} // ensureVersion tries to setup gpgme with a specific version or skip func ensureVersion(t testing.TB, version, msg string) { t.Helper() if isVersion(t, version) { return } for _, bin := range gpgBins { path, err := exec.LookPath(bin) if err != nil { continue } if err := SetEngineInfo(ProtocolOpenPGP, path, absTestGPGHome()); err != nil { continue } if isVersion(t, version) { return } } t.Skip(msg) } func diff(t testing.TB, dst, src []byte) { t.Helper() line := 1 offs := 0 // line offset for i := 0; i < len(dst) && i < len(src); i++ { d := dst[i] s := src[i] if d != s { t.Errorf("dst:%d: %s\n", line, dst[offs:i+1]) t.Errorf("src:%d: %s\n", line, src[offs:i+1]) return } if s == '\n' { line++ offs = i + 1 } } if len(dst) != len(src) { t.Errorf("len(dst) = %d, len(src) = %d\nsrc = %q", len(dst), len(src), src) } } func checkError(t testing.TB, err error) { t.Helper() if err == nil { return } t.Fatal(err) } func absTestGPGHome() string { f, err := filepath.Abs(testGPGHome) if err != nil { panic(err) } return f } gpgme-0.1.4/testdata/000077500000000000000000000000001472025072000144235ustar00rootroot00000000000000gpgme-0.1.4/testdata/gpghome/000077500000000000000000000000001472025072000160515ustar00rootroot00000000000000gpgme-0.1.4/testdata/gpghome/gpg.conf000066400000000000000000000000001472025072000174630ustar00rootroot00000000000000gpgme-0.1.4/testdata/gpghome/pubring.gpg000066400000000000000000000022431472025072000202170ustar00rootroot00000000000000 T'U -/YCXa^M85Ci `8b*AH `cϣzr`6JL1\[o oA}D/IR͕iz:q蠭} `9#T'U   '"a6!k:Ԣ@ʌ>f$E"om,}4w{I͙/яAq[wvܦH0=# S؛ :_QvA_b 17+: sFGW> wGf Rŭ;1u7B!Yym0;T09 ^g" 11p$_!%ib#BUlvM%#'?[TtO;Ʋ3 T'U '"a6ho*[6Ɨa%̥Cd'+LFZ` ;X 0*Vq+2P,Yz+2ɗFȪXȕz27Pm$J7|,aŘvR]ASLyu32hk[X/%/;uTi.ie蜩 ЗC%. R۠['pY9W[:\?Q;9Bh~sAgpgme-0.1.4/testdata/gpghome/secring.gpg000066400000000000000000000050051472025072000202020ustar00rootroot00000000000000T'U -/YCXa^M85Ci `8b*AH `cϣzr`6JL1\[o oA}D/IR͕iz:q蠭} `cuVLkܞT۰4ڍB&W6Ӂf3!󊀤Xg[gҦ:g(=_yۦ%HפȨ_Us̭IJmy GlܙN"I9E(is" kkS D(pD*+f&-uЄ_gn(ʽO}O5uNSXjOvsx ̸c~Bʿ\`Twhyf4FkUbyMOK *5"2LvmB%peAU_s?]Á]Ly Ujj3Uk\#+CLKwaoy힆U.U837l, _%Qv}dzgEJ4CZct2dMGn?۰D"FPKTAv!u}.j7e+'`m"c_ǥh^DQE&kN҆qV`/ئ_`^ /V~[Jm<(KTest Key 9#T'U   '"a6!k:Ԣ@ʌ>f$E"om,}4w{I͙/яAq[wvܦH0=# S؛ :_QvA_b 17+: sFGW> wGf Rŭ;1u7B!Yym0;T09 ^g" 11p$_!%ib#BUlvM%#'?[TtO;Ʋ3 q~sQ 5WL 0҃^zжr0CH@l9v!>s6nA5Wl,F Af&r')gX?"g;!E6i~܅CL%w=G!XC#N̤lŽ M88mZ\_5"XJ4QoߚhKs].R!5 48 w0j/Ƞuu K(c my ]LP_՚ogBXsH4ѝԁR_DAE`~C-a13~6vx?ҟKNRǥ圎j X`/sRZ(0Ybnb+0gt3tvЊZ^@sm.:{8H_h?~VԌn%dJuo:“^{6BBX{Бn2CCcxdrM#神<}c\H`9hW]fX ]6 6\DPۃZkY0(\ )՗?j`=+(#)esZbW; Wnj !/!qcijŧ})&v yYNֶ T'U '"a6ho*[6Ɨa%̥Cd'+LFZ` ;X 0*Vq+2P,Yz+2ɗFȪXȕz27Pm$J7|,aŘvR]ASLyu32hk[X/%/;uTi.ie蜩 ЗC%. R۠['pY9W[:\?Q;9Bh~sAgpgme-0.1.4/testdata/gpghome/trustdb.gpg000066400000000000000000000024001472025072000202330ustar00rootroot00000000000000gpgT'U  DF4|1gOE'"a6 jm9> KkL'gpgme-0.1.4/testdata/pubkeys.gpg000066400000000000000000000032361472025072000166100ustar00rootroot00000000000000-----BEGIN PGP PUBLIC KEY BLOCK----- Version: GnuPG v1 mQENBFQnVRMBCADJCrvBzeItL55ZQ1jgYV5NOPo1q0O/aQzUxQ9gOMBiKkFIDWBj z6PrehVyYByKNkpMMVwF2VtvqA3+pW8e+0EV633y3UQvSVLNlWmYerk683HsAeyI GZS7oOigrX0NYOw8RsqKlz3rTXC4NbMB5m7CA+L7XdsCzH9njy9D/48RuDczywHb /QWCcXO+/xMt32KqZh+/wOi5bO6v+i0HuzijoKtPGRjHg3+Ar7EIlEgU4T1TYYsO uzB+OmNF7EAALg2Qt9BBzbTnQQl7rNWH9i+kWq13BVf9Ug+CerCB05IwNSeIU71K gQlIzZfY2C+040AG5GDYDPaHB1tKaPd1Yf8JABEBAAG0G1Rlc3QgS2V5IDx0ZXN0 QGV4YW1wbGUuY29tPokBOQQTAQIAIwUCVCdVEwIbAwcLCQgHAwIBBhUIAgkKCwQW AgMBAh4BAheAAAoJEAMn/7Ain2E2IesH/2s61KJAyow+ZuX6ryRFlhm0irEi0m9t LOWa7X2qNLt31XsHSc2ZL+jRj5nnQYv7y3Fbd+gIgHb03Kap10i//ukwPbX0h+oj xAEKH9tTpRzYm+MKOrdfrB+OUXZBX63oYooLMTcSKxk6C6881Es8yFIDOVGCSUuu JUNrojPVMdRVD8MHlDPpg0SFrC4s7tLQaQ7L2sFipCPUwLuPV91ionPv7sv8q3LR D3O5CoaC/nl1XNIDVmXHACwyRWw/iCbQ9L7xbeiEgJkZLCQHanhbvFPMA4hFdQQX 6qcYh6vRzOJv+UyAORfKpIu5avnqo7GziADhf4qJVxQ0sSV/DutG9765AQ0EVCdV EwEIALyTAnCvrjPJUeFJMR9m6cSFrTVzMrZDbyydBdNtwgZM9DNUCPe+os6p6/fq UVTceICHlI8ogDAIct983mfu5xb0YPvOHIc9PnPoRrRHlVcYsT4fIHeeR2YLjg3e 6AxS/8D9Hx/FrTu9tTF1wxOuN0I8ViPKu90c8v9neCGgHkYptTj40IHXoXdlwjI7 vkls6aXb3T4hsvRZHBeGeW2vMKXYHpw7VO3+MJWiOb7s5g2KFfKzXmf3Ir3MIOXV MTGMcCRfnyElv+aCaWISByNChb3xVWx2u03fJYG3/JoGg5yPieEjnSeFP1sUklSs dNjf/k87FsfcEvMW+63LxrIzHIcAEQEAAYkBHwQYAQIACQUCVCdVEwIbDAAKCRAD J/+wIp9hNmiiB/9v2CpbNuiSxpe8YSWzwsyll0OwZCespQUr9kzGRlrtYKy7Geuj DeE7WJS9C4ULMIS2wCpWcbeHucwrB+QylONQ5yxZeivFxR8yyZev+axGyKr3qMYI pPbkhlilpwWJyJV6HzIUjTfRUKVt7ySqAo4VzNjwu7r2E5IdSjecm3ywLB5hxZjZ dgTxUghdQYS+U6JMod55dcMzMvGm8WiZqp7CaxmfW56bgfJYxfGqL+MXEYiKB/Yl /S8773XlVA7g9JOPnmkuG7NpZb3onKkcmwmq0JcOQyUuClLx26DMW6wn0XBZOe1X WzpclcM/UTuDk7w5/PVCorGZAONowX5zB0GQ =TA2b -----END PGP PUBLIC KEY BLOCK----- gpgme-0.1.4/unset_agent_info.go000066400000000000000000000010431472025072000164660ustar00rootroot00000000000000//go:build !windows // +build !windows package gpgme // #include 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) } gpgme-0.1.4/unset_agent_info_windows.go000066400000000000000000000003361472025072000202440ustar00rootroot00000000000000package gpgme // #include 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) }