// 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 ( // Unavailable in 1.3.2 // 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 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 // 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 { callbackDelete(c.cbc) } C.gpgme_release(c.ctx) runtime.KeepAlive(c) c.ctx = nil } func (c *Context) SetArmor(yes bool) { C.gpgme_set_armor(c.ctx, cbool(yes)) runtime.KeepAlive(c) } func (c *Context) Armor() bool { 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 } // Unavailable in 1.3.2: // 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 // } // Unavailable in 1.3.2: // 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 { 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) } 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 type AssuanInquireCallback func(name, args string) error type AssuanStatusCallback func(status, args string) error // AssuanSend sends a raw Assuan command to gpg-agent func (c *Context) AssuanSend( cmd string, data AssuanDataCallback, inquiry AssuanInquireCallback, status AssuanStatusCallback, ) error { var operr C.gpgme_error_t dataPtr := callbackAdd(&data) inquiryPtr := callbackAdd(&inquiry) statusPtr := callbackAdd(&status) cmdCStr := C.CString(cmd) defer C.free(unsafe.Pointer(cmdCStr)) err := C.gogpgme_op_assuan_transact_ext( c.ctx, cmdCStr, C.uintptr_t(dataPtr), C.uintptr_t(inquiryPtr), C.uintptr_t(statusPtr), &operr, ) runtime.KeepAlive(c) if handleError(operr) != nil { return handleError(operr) } return handleError(err) } //export gogpgme_assuan_data_callback func gogpgme_assuan_data_callback(handle unsafe.Pointer, data unsafe.Pointer, datalen C.size_t) C.gpgme_error_t { c := callbackLookup(uintptr(handle)).(*AssuanDataCallback) if *c == nil { return 0 } (*c)(C.GoBytes(data, C.int(datalen))) return 0 } //export gogpgme_assuan_inquiry_callback func gogpgme_assuan_inquiry_callback(handle unsafe.Pointer, cName *C.char, cArgs *C.char) C.gpgme_error_t { name := C.GoString(cName) args := C.GoString(cArgs) c := callbackLookup(uintptr(handle)).(*AssuanInquireCallback) if *c == nil { return 0 } (*c)(name, args) return 0 } //export gogpgme_assuan_status_callback func gogpgme_assuan_status_callback(handle unsafe.Pointer, cStatus *C.char, cArgs *C.char) C.gpgme_error_t { status := C.GoString(cStatus) args := C.GoString(cArgs) c := callbackLookup(uintptr(handle)).(*AssuanStatusCallback) if *c == nil { return 0 } (*c)(status, args) return 0 } // ExportModeFlags defines how keys are exported from Export type ExportModeFlags uint const ( ExportModeExtern ExportModeFlags = C.GPGME_EXPORT_MODE_EXTERN ExportModeMinimal ExportModeFlags = C.GPGME_EXPORT_MODE_MINIMAL ) func (c *Context) Export(pattern string, mode ExportModeFlags, data *Data) error { pat := C.CString(pattern) defer C.free(unsafe.Pointer(pat)) err := handleError(C.gpgme_op_export(c.ctx, pat, C.gpgme_export_mode_t(mode), data.dh)) runtime.KeepAlive(c) runtime.KeepAlive(data) return err } // ImportStatusFlags describes the type of ImportStatus.Status. The C API in gpgme.h simply uses "unsigned". 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) }