pax_global_header00006660000000000000000000000064135561507570014530gustar00rootroot0000000000000052 comment=a9039885c5ff0a34856763b8ed4406c70de6987b gpgme-0.1.0/000077500000000000000000000000001355615075700126255ustar00rootroot00000000000000gpgme-0.1.0/.appveyor.yml000066400000000000000000000024711355615075700152770ustar00rootroot00000000000000--- version: 0.{build} platform: x86 branches: only: - master clone_folder: c:\gopath\src\github.com\proglottis\gpgme environment: GOPATH: c:\gopath GOROOT: C:\go-x86 CGO_LDFLAGS: -LC:\gpg\lib CGO_CFLAGS: -IC:\gpg\include GPG_DIR: C:\gpg install: - nuget install 7ZipCLI -ExcludeVersion - set PATH=%appveyor_build_folder%\7ZipCLI\tools;%PATH% - appveyor DownloadFile https://www.gnupg.org/ftp/gcrypt/binary/gnupg-w32-2.1.20_20170403.exe -FileName gnupg-w32-2.1.20_20170403.exe - 7z x -o%GPG_DIR% gnupg-w32-2.1.20_20170403.exe - copy "%GPG_DIR%\lib\libgpg-error.imp" "%GPG_DIR%\lib\libgpg-error.a" - copy "%GPG_DIR%\lib\libassuan.imp" "%GPG_DIR%\lib\libassuan.a" - copy "%GPG_DIR%\lib\libgpgme.imp" "%GPG_DIR%\lib\libgpgme.a" - set PATH=%GOPATH%\bin;%GOROOT%\bin;%GPG_DIR%\bin;C:\MinGW\bin;%PATH% - C:\cygwin\bin\sed -i 's/"GPG_AGENT_INFO"/"GPG_AGENT_INFO="/;s/C.unsetenv(v)/C.putenv(v)/' %APPVEYOR_BUILD_FOLDER%\gpgme.go test_script: - go test -v github.com/proglottis/gpgme build_script: - go build -o example_decrypt.exe -i %APPVEYOR_BUILD_FOLDER%\examples\decrypt.go - go build -o example_encrypt.exe -i %APPVEYOR_BUILD_FOLDER%\examples\encrypt.go artifacts: - path: example_decrypt.exe name: decrypt example binary - path: example_encrypt.exe name: encrypt example binarygpgme-0.1.0/.gitignore000066400000000000000000000000351355615075700146130ustar00rootroot00000000000000testdata/gpghome/random_seed gpgme-0.1.0/.travis.yml000066400000000000000000000007041355615075700147370ustar00rootroot00000000000000--- language: go os: - linux - osx - windows dist: trusty sudo: false go: - 1.11 addons: apt: packages: - libgpgme11-dev homebrew: packages: - gnupg - gnupg@1.4 - gpgme matrix: allow_failures: - os: windows before_install: - if [[ "$TRAVIS_OS_NAME" == "windows" ]]; then choco install msys2 ; fi install: - true #do not execute "go get ./..." as it will fail with the examples script: - go test -v . gpgme-0.1.0/LICENSE000066400000000000000000000027431355615075700136400ustar00rootroot00000000000000Copyright (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.0/README.md000066400000000000000000000006451355615075700141110ustar00rootroot00000000000000# 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.0/callbacks.go000066400000000000000000000012741355615075700150770ustar00rootroot00000000000000package gpgme import ( "sync" ) var callbacks struct { sync.Mutex m map[uintptr]interface{} c uintptr } func callbackAdd(v interface{}) uintptr { callbacks.Lock() defer callbacks.Unlock() if callbacks.m == nil { callbacks.m = make(map[uintptr]interface{}) } callbacks.c++ ret := callbacks.c callbacks.m[ret] = v return ret } func callbackLookup(c uintptr) interface{} { callbacks.Lock() defer callbacks.Unlock() ret := callbacks.m[c] if ret == nil { panic("callback pointer not found") } return ret } func callbackDelete(c uintptr) { callbacks.Lock() defer callbacks.Unlock() if callbacks.m[c] == nil { panic("callback pointer not found") } delete(callbacks.m, c) } gpgme-0.1.0/data.go000066400000000000000000000112331355615075700140650ustar00rootroot00000000000000package gpgme // #include // #include // #include // #include "go_gpgme.h" import "C" import ( "io" "os" "runtime" "unsafe" ) const ( SeekSet = C.SEEK_SET SeekCur = C.SEEK_CUR SeekEnd = C.SEEK_END ) //export gogpgme_readfunc func gogpgme_readfunc(handle, buffer unsafe.Pointer, size C.size_t) C.ssize_t { d := callbackLookup(uintptr(handle)).(*Data) if len(d.buf) < int(size) { d.buf = make([]byte, size) } n, err := d.r.Read(d.buf[:size]) if err != nil && err != io.EOF { C.gpgme_err_set_errno(C.EIO) return -1 } C.memcpy(buffer, unsafe.Pointer(&d.buf[0]), C.size_t(n)) return C.ssize_t(n) } //export gogpgme_writefunc func gogpgme_writefunc(handle, buffer unsafe.Pointer, size C.size_t) C.ssize_t { d := callbackLookup(uintptr(handle)).(*Data) if len(d.buf) < int(size) { d.buf = make([]byte, size) } C.memcpy(unsafe.Pointer(&d.buf[0]), buffer, C.size_t(size)) n, err := d.w.Write(d.buf[:size]) if err != nil && err != io.EOF { 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 { 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.gpgme_off_t(n) } // The Data buffer used to communicate with GPGME type Data struct { dh C.gpgme_data_t buf []byte cbs C.struct_gpgme_data_cbs r io.Reader w io.Writer s io.Seeker cbc uintptr } 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() 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 d.cbs.read = C.gpgme_data_read_cb_t(C.gogpgme_readfunc) cbc := callbackAdd(d) d.cbc = cbc return d, handleError(C.gogpgme_data_new_from_cbs(&d.dh, &d.cbs, C.uintptr_t(cbc))) } // NewDataWriter returns a new callback based data buffer func NewDataWriter(w io.Writer) (*Data, error) { d := newData() d.w = w d.cbs.write = C.gpgme_data_write_cb_t(C.gogpgme_writefunc) cbc := callbackAdd(d) d.cbc = cbc return d, handleError(C.gogpgme_data_new_from_cbs(&d.dh, &d.cbs, C.uintptr_t(cbc))) } // NewDataReadWriter returns a new callback based data buffer func NewDataReadWriter(rw io.ReadWriter) (*Data, error) { d := newData() d.r = rw d.w = rw d.cbs.read = C.gpgme_data_read_cb_t(C.gogpgme_readfunc) d.cbs.write = C.gpgme_data_write_cb_t(C.gogpgme_writefunc) cbc := callbackAdd(d) d.cbc = cbc return d, handleError(C.gogpgme_data_new_from_cbs(&d.dh, &d.cbs, C.uintptr_t(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.cbs.read = C.gpgme_data_read_cb_t(C.gogpgme_readfunc) d.cbs.write = C.gpgme_data_write_cb_t(C.gogpgme_writefunc) d.cbs.seek = C.gpgme_data_seek_cb_t(C.gogpgme_seekfunc) cbc := callbackAdd(d) d.cbc = cbc return d, handleError(C.gogpgme_data_new_from_cbs(&d.dh, &d.cbs, C.uintptr_t(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 { callbackDelete(d.cbc) } _, err := C.gpgme_data_release(d.dh) 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))) if err != nil { return 0, err } if n == 0 { return 0, io.EOF } return int(n), nil } 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))) if err != nil { return 0, err } if 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)) 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)) } gpgme-0.1.0/data_test.go000066400000000000000000000035361355615075700151330ustar00rootroot00000000000000package gpgme import ( "bytes" "io" "io/ioutil" "os" "testing" ) func TestNewData(t *testing.T) { dh, err := NewData() checkError(t, err) for i := 0; i < 5; i++ { _, err := dh.Write([]byte(testData)) checkError(t, err) } _, err = dh.Seek(0, SeekSet) checkError(t, err) var buf bytes.Buffer _, err = io.Copy(&buf, dh) checkError(t, err) expected := bytes.Repeat([]byte(testData), 5) diff(t, buf.Bytes(), expected) dh.Close() } func TestNewDataBytes(t *testing.T) { // Test ordinary data, and empty slices for _, content := range [][]byte{[]byte("content"), []byte{}} { dh, err := NewDataBytes(content) checkError(t, err) _, err = dh.Seek(0, SeekSet) checkError(t, err) var buf bytes.Buffer _, err = io.Copy(&buf, dh) checkError(t, err) diff(t, buf.Bytes(), content) } } func TestDataNewDataFile(t *testing.T) { f, err := ioutil.TempFile("", "gpgme") checkError(t, err) defer func() { f.Close() os.Remove(f.Name()) }() dh, err := NewDataFile(f) checkError(t, err) defer dh.Close() for i := 0; i < 5; i++ { _, err := dh.Write([]byte(testData)) checkError(t, err) } _, err = dh.Seek(0, SeekSet) checkError(t, err) var buf bytes.Buffer _, err = io.Copy(&buf, dh) checkError(t, err) expected := bytes.Repeat([]byte(testData), 5) diff(t, buf.Bytes(), expected) } func TestDataNewDataReader(t *testing.T) { r := bytes.NewReader([]byte(testData)) dh, err := NewDataReader(r) checkError(t, err) var buf bytes.Buffer _, err = io.Copy(&buf, dh) checkError(t, err) diff(t, buf.Bytes(), []byte(testData)) dh.Close() } func TestDataNewDataWriter(t *testing.T) { var buf bytes.Buffer dh, err := NewDataWriter(&buf) checkError(t, err) for i := 0; i < 5; i++ { _, err := dh.Write([]byte(testData)) checkError(t, err) } expected := bytes.Repeat([]byte(testData), 5) diff(t, buf.Bytes(), expected) dh.Close() } gpgme-0.1.0/examples/000077500000000000000000000000001355615075700144435ustar00rootroot00000000000000gpgme-0.1.0/examples/decrypt.go000066400000000000000000000003711355615075700164450ustar00rootroot00000000000000package main import ( "io" "os" "github.com/proglottis/gpgme" ) func main() { plain, err := gpgme.Decrypt(os.Stdin) if err != nil { panic(err) } defer plain.Close() if _, err := io.Copy(os.Stdout, plain); err != nil { panic(err) } } gpgme-0.1.0/examples/encrypt.go000066400000000000000000000012041355615075700164530ustar00rootroot00000000000000package main import ( "flag" "os" "github.com/proglottis/gpgme" ) func main() { flag.Parse() filter := flag.Arg(0) if filter == "" { panic("must specify recipient filter") } recipients, err := gpgme.FindKeys(filter, false) if err != nil { panic(err) } if len(recipients) < 1 { panic("no keys match") } plain, err := gpgme.NewDataReader(os.Stdin) if err != nil { panic(err) } cipher, err := gpgme.NewDataWriter(os.Stdout) if err != nil { panic(err) } ctx, err := gpgme.New() if err != nil { panic(err) } ctx.SetArmor(true) if err := ctx.Encrypt(recipients, 0, plain, cipher); err != nil { panic(err) } } gpgme-0.1.0/go.mod000066400000000000000000000000541355615075700137320ustar00rootroot00000000000000module github.com/proglottis/gpgme go 1.11 gpgme-0.1.0/go_gpgme.c000066400000000000000000000045341355615075700145630ustar00rootroot00000000000000#include "go_gpgme.h" gpgme_error_t gogpgme_data_new_from_cbs(gpgme_data_t *dh, gpgme_data_cbs_t cbs, uintptr_t handle) { return gpgme_data_new_from_cbs(dh, cbs, (void *)handle); } void gogpgme_set_passphrase_cb(gpgme_ctx_t ctx, gpgme_passphrase_cb_t cb, uintptr_t handle) { 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; } 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.0/go_gpgme.h000066400000000000000000000043261355615075700145670ustar00rootroot00000000000000#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_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); 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.0/gpgme.go000066400000000000000000000466061355615075700142670ustar00rootroot00000000000000// Package gpgme provides a Go wrapper for the GPGME library package gpgme // #cgo LDFLAGS: -lgpgme -lassuan -lgpg-error // #cgo CPPFLAGS: -D_FILE_OFFSET_BITS=64 // #include // #include // #include "go_gpgme.h" import "C" import ( "fmt" "io" "os" "runtime" "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 { c := callbackLookup(uintptr(hook)).(*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 { info C.gpgme_engine_info_t } func (e *EngineInfo) Next() *EngineInfo { if e.info.next == nil { return nil } return &EngineInfo{info: e.info.next} } func (e *EngineInfo) Protocol() Protocol { return Protocol(e.info.protocol) } func (e *EngineInfo) FileName() string { return C.GoString(e.info.file_name) } func (e *EngineInfo) Version() string { return C.GoString(e.info.version) } func (e *EngineInfo) RequiredVersion() string { return C.GoString(e.info.req_version) } func (e *EngineInfo) HomeDir() string { return C.GoString(e.info.home_dir) } func GetEngineInfo() (*EngineInfo, error) { info := &EngineInfo{} return info, handleError(C.gpgme_get_engine_info(&info.info)) } 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 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 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 } err = ctx.Decrypt(cipher, plain) plain.Seek(0, SeekSet) return plain, err } type Context struct { Key *Key KeyError error callback Callback cbc uintptr ctx C.gpgme_ctx_t } 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 { callbackDelete(c.cbc) } C.gpgme_release(c.ctx) c.ctx = nil } func (c *Context) SetArmor(yes bool) { C.gpgme_set_armor(c.ctx, cbool(yes)) } func (c *Context) Armor() bool { return C.gpgme_get_armor(c.ctx) != 0 } func (c *Context) SetTextMode(yes bool) { C.gpgme_set_textmode(c.ctx, cbool(yes)) } func (c *Context) TextMode() bool { return C.gpgme_get_textmode(c.ctx) != 0 } func (c *Context) SetProtocol(p Protocol) error { return handleError(C.gpgme_set_protocol(c.ctx, C.gpgme_protocol_t(p))) } func (c *Context) Protocol() Protocol { return Protocol(C.gpgme_get_protocol(c.ctx)) } func (c *Context) SetKeyListMode(m KeyListMode) error { return handleError(C.gpgme_set_keylist_mode(c.ctx, C.gpgme_keylist_mode_t(m))) } func (c *Context) KeyListMode() KeyListMode { return KeyListMode(C.gpgme_get_keylist_mode(c.ctx)) } func (c *Context) SetPinEntryMode(m PinEntryMode) error { return handleError(C.gpgme_set_pinentry_mode(c.ctx, C.gpgme_pinentry_mode_t(m))) } func (c *Context) PinEntryMode() PinEntryMode { return PinEntryMode(C.gpgme_get_pinentry_mode(c.ctx)) } func (c *Context) SetCallback(callback Callback) error { var err error c.callback = callback if c.cbc > 0 { callbackDelete(c.cbc) } if callback != nil { cbc := callbackAdd(c) c.cbc = cbc _, err = C.gogpgme_set_passphrase_cb(c.ctx, C.gpgme_passphrase_cb_t(C.gogpgme_passfunc), C.uintptr_t(cbc)) } else { c.cbc = 0 _, err = C.gogpgme_set_passphrase_cb(c.ctx, nil, 0) } return err } func (c *Context) EngineInfo() *EngineInfo { return &EngineInfo{info: C.gpgme_ctx_get_engine_info(c.ctx)} } 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)) } return handleError(C.gpgme_ctx_set_engine_info(c.ctx, C.gpgme_protocol_t(proto), cfn, chome)) } 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) } func (c *Context) KeyListNext() bool { c.Key = newKey() err := handleError(C.gpgme_op_keylist_next(c.ctx, &c.Key.k)) 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 { return handleError(C.gpgme_op_keylist_end(c.ctx)) } 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))) if e, ok := err.(Error); key.k == nil && 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 { return handleError(C.gpgme_op_decrypt(c.ctx, ciphertext.dh, plaintext.dh)) } func (c *Context) DecryptVerify(ciphertext, plaintext *Data) error { return handleError(C.gpgme_op_decrypt_verify(c.ctx, ciphertext.dh, plaintext.dh)) } 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)) if err != nil { return "", nil, err } res := C.gpgme_op_verify_result(c.ctx) 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) } return C.GoString(res.file_name), 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) return handleError(err) } func (c *Context) Sign(signers []*Key, plain, sig *Data, mode SigMode) error { C.gpgme_signers_clear(c.ctx) for _, k := range signers { if err := handleError(C.gpgme_signers_add(c.ctx, k.k)); err != nil { C.gpgme_signers_clear(c.ctx) return err } } return handleError(C.gpgme_op_sign(c.ctx, plain.dh, sig.dh, C.gpgme_sig_mode_t(mode))) } 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, ) 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)) return handleError(C.gpgme_op_export(c.ctx, pat, C.gpgme_export_mode_t(mode), data.dh)) } // 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)) if err != nil { return nil, err } res := C.gpgme_op_import_result(c.ctx) 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), }) } return &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, }, nil } type Key struct { k C.gpgme_key_t } func newKey() *Key { k := &Key{} runtime.SetFinalizer(k, (*Key).Release) return k } func (k *Key) Release() { C.gpgme_key_release(k.k) k.k = nil } func (k *Key) Revoked() bool { return C.key_revoked(k.k) != 0 } func (k *Key) Expired() bool { return C.key_expired(k.k) != 0 } func (k *Key) Disabled() bool { return C.key_disabled(k.k) != 0 } func (k *Key) Invalid() bool { return C.key_invalid(k.k) != 0 } func (k *Key) CanEncrypt() bool { return C.key_can_encrypt(k.k) != 0 } func (k *Key) CanSign() bool { return C.key_can_sign(k.k) != 0 } func (k *Key) CanCertify() bool { return C.key_can_certify(k.k) != 0 } func (k *Key) Secret() bool { return C.key_secret(k.k) != 0 } func (k *Key) CanAuthenticate() bool { return C.key_can_authenticate(k.k) != 0 } func (k *Key) IsQualified() bool { return C.key_is_qualified(k.k) != 0 } func (k *Key) Protocol() Protocol { return Protocol(k.k.protocol) } func (k *Key) IssuerSerial() string { return C.GoString(k.k.issuer_serial) } func (k *Key) IssuerName() string { return C.GoString(k.k.issuer_name) } func (k *Key) ChainID() string { return C.GoString(k.k.chain_id) } func (k *Key) OwnerTrust() Validity { return Validity(k.k.owner_trust) } func (k *Key) SubKeys() *SubKey { if k.k.subkeys == nil { return nil } return &SubKey{k: k.k.subkeys, parent: k} } func (k *Key) UserIDs() *UserID { if k.k.uids == nil { return nil } return &UserID{u: k.k.uids, parent: k} } func (k *Key) KeyListMode() KeyListMode { return KeyListMode(k.k.keylist_mode) } 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.0/gpgme_test.go000066400000000000000000000272111355615075700153150ustar00rootroot00000000000000package 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 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.T, 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.0/testdata/000077500000000000000000000000001355615075700144365ustar00rootroot00000000000000gpgme-0.1.0/testdata/gpghome/000077500000000000000000000000001355615075700160645ustar00rootroot00000000000000gpgme-0.1.0/testdata/gpghome/gpg.conf000066400000000000000000000000001355615075700174760ustar00rootroot00000000000000gpgme-0.1.0/testdata/gpghome/pubring.gpg000066400000000000000000000022431355615075700202320ustar00rootroot00000000000000 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.0/testdata/gpghome/secring.gpg000066400000000000000000000050051355615075700202150ustar00rootroot00000000000000T'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.0/testdata/gpghome/trustdb.gpg000066400000000000000000000024001355615075700202460ustar00rootroot00000000000000gpgT'U  DF4|1gOE'"a6 jm9> KkL'gpgme-0.1.0/testdata/pubkeys.gpg000066400000000000000000000032361355615075700166230ustar00rootroot00000000000000-----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.0/unset_agent_info.go000066400000000000000000000010171355615075700165020ustar00rootroot00000000000000// +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.0/unset_agent_info_windows.go000066400000000000000000000003361355615075700202570ustar00rootroot00000000000000package 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) }