diff options
Diffstat (limited to 'vendor/github.com/proglottis/gpgme/gpgme.go')
-rw-r--r-- | vendor/github.com/proglottis/gpgme/gpgme.go | 950 |
1 files changed, 950 insertions, 0 deletions
diff --git a/vendor/github.com/proglottis/gpgme/gpgme.go b/vendor/github.com/proglottis/gpgme/gpgme.go new file mode 100644 index 000000000..9833057a6 --- /dev/null +++ b/vendor/github.com/proglottis/gpgme/gpgme.go @@ -0,0 +1,950 @@ +// 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 <stdlib.h> +// #include <gpgme.h> +// #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 { + 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 +} + +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 { + 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) +} |