podman
950 строк · 22.7 Кб
1// Package gpgme provides a Go wrapper for the GPGME library
2package gpgme
3
4// #cgo pkg-config: gpgme
5// #cgo CPPFLAGS: -D_FILE_OFFSET_BITS=64
6// #include <stdlib.h>
7// #include <gpgme.h>
8// #include "go_gpgme.h"
9import "C"
10import (
11"fmt"
12"io"
13"os"
14"runtime"
15"time"
16"unsafe"
17)
18
19var Version string
20
21func init() {
22Version = C.GoString(C.gpgme_check_version(nil))
23}
24
25// Callback is the function that is called when a passphrase is required
26type Callback func(uidHint string, prevWasBad bool, f *os.File) error
27
28//export gogpgme_passfunc
29func gogpgme_passfunc(hook unsafe.Pointer, uid_hint, passphrase_info *C.char, prev_was_bad, fd C.int) C.gpgme_error_t {
30c := callbackLookup(uintptr(hook)).(*Context)
31go_uid_hint := C.GoString(uid_hint)
32f := os.NewFile(uintptr(fd), go_uid_hint)
33defer f.Close()
34err := c.callback(go_uid_hint, prev_was_bad != 0, f)
35if err != nil {
36return C.GPG_ERR_CANCELED
37}
38return 0
39}
40
41type Protocol int
42
43const (
44ProtocolOpenPGP Protocol = C.GPGME_PROTOCOL_OpenPGP
45ProtocolCMS Protocol = C.GPGME_PROTOCOL_CMS
46ProtocolGPGConf Protocol = C.GPGME_PROTOCOL_GPGCONF
47ProtocolAssuan Protocol = C.GPGME_PROTOCOL_ASSUAN
48ProtocolG13 Protocol = C.GPGME_PROTOCOL_G13
49ProtocolUIServer Protocol = C.GPGME_PROTOCOL_UISERVER
50ProtocolDefault Protocol = C.GPGME_PROTOCOL_DEFAULT
51ProtocolUnknown Protocol = C.GPGME_PROTOCOL_UNKNOWN
52)
53
54type PinEntryMode int
55
56const (
57PinEntryDefault PinEntryMode = C.GPGME_PINENTRY_MODE_DEFAULT
58PinEntryAsk PinEntryMode = C.GPGME_PINENTRY_MODE_ASK
59PinEntryCancel PinEntryMode = C.GPGME_PINENTRY_MODE_CANCEL
60PinEntryError PinEntryMode = C.GPGME_PINENTRY_MODE_ERROR
61PinEntryLoopback PinEntryMode = C.GPGME_PINENTRY_MODE_LOOPBACK
62)
63
64type EncryptFlag uint
65
66const (
67EncryptAlwaysTrust EncryptFlag = C.GPGME_ENCRYPT_ALWAYS_TRUST
68EncryptNoEncryptTo EncryptFlag = C.GPGME_ENCRYPT_NO_ENCRYPT_TO
69EncryptPrepare EncryptFlag = C.GPGME_ENCRYPT_PREPARE
70EncryptExceptSign EncryptFlag = C.GPGME_ENCRYPT_EXPECT_SIGN
71)
72
73type HashAlgo int
74
75// const values for HashAlgo values should be added when necessary.
76
77type KeyListMode uint
78
79const (
80KeyListModeLocal KeyListMode = C.GPGME_KEYLIST_MODE_LOCAL
81KeyListModeExtern KeyListMode = C.GPGME_KEYLIST_MODE_EXTERN
82KeyListModeSigs KeyListMode = C.GPGME_KEYLIST_MODE_SIGS
83KeyListModeSigNotations KeyListMode = C.GPGME_KEYLIST_MODE_SIG_NOTATIONS
84KeyListModeEphemeral KeyListMode = C.GPGME_KEYLIST_MODE_EPHEMERAL
85KeyListModeModeValidate KeyListMode = C.GPGME_KEYLIST_MODE_VALIDATE
86)
87
88type PubkeyAlgo int
89
90// const values for PubkeyAlgo values should be added when necessary.
91
92type SigMode int
93
94const (
95SigModeNormal SigMode = C.GPGME_SIG_MODE_NORMAL
96SigModeDetach SigMode = C.GPGME_SIG_MODE_DETACH
97SigModeClear SigMode = C.GPGME_SIG_MODE_CLEAR
98)
99
100type SigSum int
101
102const (
103SigSumValid SigSum = C.GPGME_SIGSUM_VALID
104SigSumGreen SigSum = C.GPGME_SIGSUM_GREEN
105SigSumRed SigSum = C.GPGME_SIGSUM_RED
106SigSumKeyRevoked SigSum = C.GPGME_SIGSUM_KEY_REVOKED
107SigSumKeyExpired SigSum = C.GPGME_SIGSUM_KEY_EXPIRED
108SigSumSigExpired SigSum = C.GPGME_SIGSUM_SIG_EXPIRED
109SigSumKeyMissing SigSum = C.GPGME_SIGSUM_KEY_MISSING
110SigSumCRLMissing SigSum = C.GPGME_SIGSUM_CRL_MISSING
111SigSumCRLTooOld SigSum = C.GPGME_SIGSUM_CRL_TOO_OLD
112SigSumBadPolicy SigSum = C.GPGME_SIGSUM_BAD_POLICY
113SigSumSysError SigSum = C.GPGME_SIGSUM_SYS_ERROR
114)
115
116type Validity int
117
118const (
119ValidityUnknown Validity = C.GPGME_VALIDITY_UNKNOWN
120ValidityUndefined Validity = C.GPGME_VALIDITY_UNDEFINED
121ValidityNever Validity = C.GPGME_VALIDITY_NEVER
122ValidityMarginal Validity = C.GPGME_VALIDITY_MARGINAL
123ValidityFull Validity = C.GPGME_VALIDITY_FULL
124ValidityUltimate Validity = C.GPGME_VALIDITY_ULTIMATE
125)
126
127type ErrorCode int
128
129const (
130ErrorNoError ErrorCode = C.GPG_ERR_NO_ERROR
131ErrorEOF ErrorCode = C.GPG_ERR_EOF
132)
133
134// Error is a wrapper for GPGME errors
135type Error struct {
136err C.gpgme_error_t
137}
138
139func (e Error) Code() ErrorCode {
140return ErrorCode(C.gpgme_err_code(e.err))
141}
142
143func (e Error) Error() string {
144return C.GoString(C.gpgme_strerror(e.err))
145}
146
147func handleError(err C.gpgme_error_t) error {
148e := Error{err: err}
149if e.Code() == ErrorNoError {
150return nil
151}
152return e
153}
154
155func cbool(b bool) C.int {
156if b {
157return 1
158}
159return 0
160}
161
162func EngineCheckVersion(p Protocol) error {
163return handleError(C.gpgme_engine_check_version(C.gpgme_protocol_t(p)))
164}
165
166type EngineInfo struct {
167next *EngineInfo
168protocol Protocol
169fileName string
170homeDir string
171version string
172requiredVersion string
173}
174
175func copyEngineInfo(info C.gpgme_engine_info_t) *EngineInfo {
176res := &EngineInfo{
177next: nil,
178protocol: Protocol(info.protocol),
179fileName: C.GoString(info.file_name),
180homeDir: C.GoString(info.home_dir),
181version: C.GoString(info.version),
182requiredVersion: C.GoString(info.req_version),
183}
184if info.next != nil {
185res.next = copyEngineInfo(info.next)
186}
187return res
188}
189
190func (e *EngineInfo) Next() *EngineInfo {
191return e.next
192}
193
194func (e *EngineInfo) Protocol() Protocol {
195return e.protocol
196}
197
198func (e *EngineInfo) FileName() string {
199return e.fileName
200}
201
202func (e *EngineInfo) Version() string {
203return e.version
204}
205
206func (e *EngineInfo) RequiredVersion() string {
207return e.requiredVersion
208}
209
210func (e *EngineInfo) HomeDir() string {
211return e.homeDir
212}
213
214func GetEngineInfo() (*EngineInfo, error) {
215var cInfo C.gpgme_engine_info_t
216err := handleError(C.gpgme_get_engine_info(&cInfo))
217if err != nil {
218return nil, err
219}
220return copyEngineInfo(cInfo), nil // It is up to the caller not to invalidate cInfo concurrently until this is done.
221}
222
223func SetEngineInfo(proto Protocol, fileName, homeDir string) error {
224var cfn, chome *C.char
225if fileName != "" {
226cfn = C.CString(fileName)
227defer C.free(unsafe.Pointer(cfn))
228}
229if homeDir != "" {
230chome = C.CString(homeDir)
231defer C.free(unsafe.Pointer(chome))
232}
233return handleError(C.gpgme_set_engine_info(C.gpgme_protocol_t(proto), cfn, chome))
234}
235
236func FindKeys(pattern string, secretOnly bool) ([]*Key, error) {
237var keys []*Key
238ctx, err := New()
239if err != nil {
240return keys, err
241}
242defer ctx.Release()
243if err := ctx.KeyListStart(pattern, secretOnly); err != nil {
244return keys, err
245}
246defer ctx.KeyListEnd()
247for ctx.KeyListNext() {
248keys = append(keys, ctx.Key)
249}
250if ctx.KeyError != nil {
251return keys, ctx.KeyError
252}
253return keys, nil
254}
255
256func Decrypt(r io.Reader) (*Data, error) {
257ctx, err := New()
258if err != nil {
259return nil, err
260}
261defer ctx.Release()
262cipher, err := NewDataReader(r)
263if err != nil {
264return nil, err
265}
266defer cipher.Close()
267plain, err := NewData()
268if err != nil {
269return nil, err
270}
271err = ctx.Decrypt(cipher, plain)
272plain.Seek(0, SeekSet)
273return plain, err
274}
275
276type Context struct {
277Key *Key
278KeyError error
279
280callback Callback
281cbc uintptr // WARNING: Call runtime.KeepAlive(c) after ANY use of c.cbc in C (typically via c.ctx)
282
283ctx C.gpgme_ctx_t // WARNING: Call runtime.KeepAlive(c) after ANY passing of c.ctx to C
284}
285
286func New() (*Context, error) {
287c := &Context{}
288err := C.gpgme_new(&c.ctx)
289runtime.SetFinalizer(c, (*Context).Release)
290return c, handleError(err)
291}
292
293func (c *Context) Release() {
294if c.ctx == nil {
295return
296}
297if c.cbc > 0 {
298callbackDelete(c.cbc)
299}
300C.gpgme_release(c.ctx)
301runtime.KeepAlive(c)
302c.ctx = nil
303}
304
305func (c *Context) SetArmor(yes bool) {
306C.gpgme_set_armor(c.ctx, cbool(yes))
307runtime.KeepAlive(c)
308}
309
310func (c *Context) Armor() bool {
311res := C.gpgme_get_armor(c.ctx) != 0
312runtime.KeepAlive(c)
313return res
314}
315
316func (c *Context) SetTextMode(yes bool) {
317C.gpgme_set_textmode(c.ctx, cbool(yes))
318runtime.KeepAlive(c)
319}
320
321func (c *Context) TextMode() bool {
322res := C.gpgme_get_textmode(c.ctx) != 0
323runtime.KeepAlive(c)
324return res
325}
326
327func (c *Context) SetProtocol(p Protocol) error {
328err := handleError(C.gpgme_set_protocol(c.ctx, C.gpgme_protocol_t(p)))
329runtime.KeepAlive(c)
330return err
331}
332
333func (c *Context) Protocol() Protocol {
334res := Protocol(C.gpgme_get_protocol(c.ctx))
335runtime.KeepAlive(c)
336return res
337}
338
339func (c *Context) SetKeyListMode(m KeyListMode) error {
340err := handleError(C.gpgme_set_keylist_mode(c.ctx, C.gpgme_keylist_mode_t(m)))
341runtime.KeepAlive(c)
342return err
343}
344
345func (c *Context) KeyListMode() KeyListMode {
346res := KeyListMode(C.gpgme_get_keylist_mode(c.ctx))
347runtime.KeepAlive(c)
348return res
349}
350
351func (c *Context) SetPinEntryMode(m PinEntryMode) error {
352err := handleError(C.gpgme_set_pinentry_mode(c.ctx, C.gpgme_pinentry_mode_t(m)))
353runtime.KeepAlive(c)
354return err
355}
356
357func (c *Context) PinEntryMode() PinEntryMode {
358res := PinEntryMode(C.gpgme_get_pinentry_mode(c.ctx))
359runtime.KeepAlive(c)
360return res
361}
362
363func (c *Context) SetCallback(callback Callback) error {
364var err error
365c.callback = callback
366if c.cbc > 0 {
367callbackDelete(c.cbc)
368}
369if callback != nil {
370cbc := callbackAdd(c)
371c.cbc = cbc
372_, err = C.gogpgme_set_passphrase_cb(c.ctx, C.gpgme_passphrase_cb_t(C.gogpgme_passfunc), C.uintptr_t(cbc))
373} else {
374c.cbc = 0
375_, err = C.gogpgme_set_passphrase_cb(c.ctx, nil, 0)
376}
377runtime.KeepAlive(c)
378return err
379}
380
381func (c *Context) EngineInfo() *EngineInfo {
382cInfo := C.gpgme_ctx_get_engine_info(c.ctx)
383runtime.KeepAlive(c)
384// NOTE: c must be live as long as we are accessing cInfo.
385res := copyEngineInfo(cInfo)
386runtime.KeepAlive(c) // for accesses to cInfo
387return res
388}
389
390func (c *Context) SetEngineInfo(proto Protocol, fileName, homeDir string) error {
391var cfn, chome *C.char
392if fileName != "" {
393cfn = C.CString(fileName)
394defer C.free(unsafe.Pointer(cfn))
395}
396if homeDir != "" {
397chome = C.CString(homeDir)
398defer C.free(unsafe.Pointer(chome))
399}
400err := handleError(C.gpgme_ctx_set_engine_info(c.ctx, C.gpgme_protocol_t(proto), cfn, chome))
401runtime.KeepAlive(c)
402return err
403}
404
405func (c *Context) KeyListStart(pattern string, secretOnly bool) error {
406cpattern := C.CString(pattern)
407defer C.free(unsafe.Pointer(cpattern))
408err := handleError(C.gpgme_op_keylist_start(c.ctx, cpattern, cbool(secretOnly)))
409runtime.KeepAlive(c)
410return err
411}
412
413func (c *Context) KeyListNext() bool {
414c.Key = newKey()
415err := handleError(C.gpgme_op_keylist_next(c.ctx, &c.Key.k))
416runtime.KeepAlive(c) // implies runtime.KeepAlive(c.Key)
417if err != nil {
418if e, ok := err.(Error); ok && e.Code() == ErrorEOF {
419c.KeyError = nil
420} else {
421c.KeyError = err
422}
423return false
424}
425c.KeyError = nil
426return true
427}
428
429func (c *Context) KeyListEnd() error {
430err := handleError(C.gpgme_op_keylist_end(c.ctx))
431runtime.KeepAlive(c)
432return err
433}
434
435func (c *Context) GetKey(fingerprint string, secret bool) (*Key, error) {
436key := newKey()
437cfpr := C.CString(fingerprint)
438defer C.free(unsafe.Pointer(cfpr))
439err := handleError(C.gpgme_get_key(c.ctx, cfpr, &key.k, cbool(secret)))
440runtime.KeepAlive(c)
441runtime.KeepAlive(key)
442keyKIsNil := key.k == nil
443runtime.KeepAlive(key)
444if e, ok := err.(Error); keyKIsNil && ok && e.Code() == ErrorEOF {
445return nil, fmt.Errorf("key %q not found", fingerprint)
446}
447if err != nil {
448return nil, err
449}
450return key, nil
451}
452
453func (c *Context) Decrypt(ciphertext, plaintext *Data) error {
454err := handleError(C.gpgme_op_decrypt(c.ctx, ciphertext.dh, plaintext.dh))
455runtime.KeepAlive(c)
456runtime.KeepAlive(ciphertext)
457runtime.KeepAlive(plaintext)
458return err
459}
460
461func (c *Context) DecryptVerify(ciphertext, plaintext *Data) error {
462err := handleError(C.gpgme_op_decrypt_verify(c.ctx, ciphertext.dh, plaintext.dh))
463runtime.KeepAlive(c)
464runtime.KeepAlive(ciphertext)
465runtime.KeepAlive(plaintext)
466return err
467}
468
469type Signature struct {
470Summary SigSum
471Fingerprint string
472Status error
473Timestamp time.Time
474ExpTimestamp time.Time
475WrongKeyUsage bool
476PKATrust uint
477ChainModel bool
478Validity Validity
479ValidityReason error
480PubkeyAlgo PubkeyAlgo
481HashAlgo HashAlgo
482}
483
484func (c *Context) Verify(sig, signedText, plain *Data) (string, []Signature, error) {
485var signedTextPtr, plainPtr C.gpgme_data_t = nil, nil
486if signedText != nil {
487signedTextPtr = signedText.dh
488}
489if plain != nil {
490plainPtr = plain.dh
491}
492err := handleError(C.gpgme_op_verify(c.ctx, sig.dh, signedTextPtr, plainPtr))
493runtime.KeepAlive(c)
494runtime.KeepAlive(sig)
495if signedText != nil {
496runtime.KeepAlive(signedText)
497}
498if plain != nil {
499runtime.KeepAlive(plain)
500}
501if err != nil {
502return "", nil, err
503}
504res := C.gpgme_op_verify_result(c.ctx)
505runtime.KeepAlive(c)
506// NOTE: c must be live as long as we are accessing res.
507sigs := []Signature{}
508for s := res.signatures; s != nil; s = s.next {
509sig := Signature{
510Summary: SigSum(s.summary),
511Fingerprint: C.GoString(s.fpr),
512Status: handleError(s.status),
513// s.notations not implemented
514Timestamp: time.Unix(int64(s.timestamp), 0),
515ExpTimestamp: time.Unix(int64(s.exp_timestamp), 0),
516WrongKeyUsage: C.signature_wrong_key_usage(s) != 0,
517PKATrust: uint(C.signature_pka_trust(s)),
518ChainModel: C.signature_chain_model(s) != 0,
519Validity: Validity(s.validity),
520ValidityReason: handleError(s.validity_reason),
521PubkeyAlgo: PubkeyAlgo(s.pubkey_algo),
522HashAlgo: HashAlgo(s.hash_algo),
523}
524sigs = append(sigs, sig)
525}
526fileName := C.GoString(res.file_name)
527runtime.KeepAlive(c) // for all accesses to res above
528return fileName, sigs, nil
529}
530
531func (c *Context) Encrypt(recipients []*Key, flags EncryptFlag, plaintext, ciphertext *Data) error {
532size := unsafe.Sizeof(new(C.gpgme_key_t))
533recp := C.calloc(C.size_t(len(recipients)+1), C.size_t(size))
534defer C.free(recp)
535for i := range recipients {
536ptr := (*C.gpgme_key_t)(unsafe.Pointer(uintptr(recp) + size*uintptr(i)))
537*ptr = recipients[i].k
538}
539err := C.gpgme_op_encrypt(c.ctx, (*C.gpgme_key_t)(recp), C.gpgme_encrypt_flags_t(flags), plaintext.dh, ciphertext.dh)
540runtime.KeepAlive(c)
541runtime.KeepAlive(recipients)
542runtime.KeepAlive(plaintext)
543runtime.KeepAlive(ciphertext)
544return handleError(err)
545}
546
547func (c *Context) Sign(signers []*Key, plain, sig *Data, mode SigMode) error {
548C.gpgme_signers_clear(c.ctx)
549runtime.KeepAlive(c)
550for _, k := range signers {
551err := handleError(C.gpgme_signers_add(c.ctx, k.k))
552runtime.KeepAlive(c)
553runtime.KeepAlive(k)
554if err != nil {
555C.gpgme_signers_clear(c.ctx)
556runtime.KeepAlive(c)
557return err
558}
559}
560err := handleError(C.gpgme_op_sign(c.ctx, plain.dh, sig.dh, C.gpgme_sig_mode_t(mode)))
561runtime.KeepAlive(c)
562runtime.KeepAlive(plain)
563runtime.KeepAlive(sig)
564return err
565}
566
567type AssuanDataCallback func(data []byte) error
568type AssuanInquireCallback func(name, args string) error
569type AssuanStatusCallback func(status, args string) error
570
571// AssuanSend sends a raw Assuan command to gpg-agent
572func (c *Context) AssuanSend(
573cmd string,
574data AssuanDataCallback,
575inquiry AssuanInquireCallback,
576status AssuanStatusCallback,
577) error {
578var operr C.gpgme_error_t
579
580dataPtr := callbackAdd(&data)
581inquiryPtr := callbackAdd(&inquiry)
582statusPtr := callbackAdd(&status)
583cmdCStr := C.CString(cmd)
584defer C.free(unsafe.Pointer(cmdCStr))
585err := C.gogpgme_op_assuan_transact_ext(
586c.ctx,
587cmdCStr,
588C.uintptr_t(dataPtr),
589C.uintptr_t(inquiryPtr),
590C.uintptr_t(statusPtr),
591&operr,
592)
593runtime.KeepAlive(c)
594
595if handleError(operr) != nil {
596return handleError(operr)
597}
598return handleError(err)
599}
600
601//export gogpgme_assuan_data_callback
602func gogpgme_assuan_data_callback(handle unsafe.Pointer, data unsafe.Pointer, datalen C.size_t) C.gpgme_error_t {
603c := callbackLookup(uintptr(handle)).(*AssuanDataCallback)
604if *c == nil {
605return 0
606}
607(*c)(C.GoBytes(data, C.int(datalen)))
608return 0
609}
610
611//export gogpgme_assuan_inquiry_callback
612func gogpgme_assuan_inquiry_callback(handle unsafe.Pointer, cName *C.char, cArgs *C.char) C.gpgme_error_t {
613name := C.GoString(cName)
614args := C.GoString(cArgs)
615c := callbackLookup(uintptr(handle)).(*AssuanInquireCallback)
616if *c == nil {
617return 0
618}
619(*c)(name, args)
620return 0
621}
622
623//export gogpgme_assuan_status_callback
624func gogpgme_assuan_status_callback(handle unsafe.Pointer, cStatus *C.char, cArgs *C.char) C.gpgme_error_t {
625status := C.GoString(cStatus)
626args := C.GoString(cArgs)
627c := callbackLookup(uintptr(handle)).(*AssuanStatusCallback)
628if *c == nil {
629return 0
630}
631(*c)(status, args)
632return 0
633}
634
635// ExportModeFlags defines how keys are exported from Export
636type ExportModeFlags uint
637
638const (
639ExportModeExtern ExportModeFlags = C.GPGME_EXPORT_MODE_EXTERN
640ExportModeMinimal ExportModeFlags = C.GPGME_EXPORT_MODE_MINIMAL
641)
642
643func (c *Context) Export(pattern string, mode ExportModeFlags, data *Data) error {
644pat := C.CString(pattern)
645defer C.free(unsafe.Pointer(pat))
646err := handleError(C.gpgme_op_export(c.ctx, pat, C.gpgme_export_mode_t(mode), data.dh))
647runtime.KeepAlive(c)
648runtime.KeepAlive(data)
649return err
650}
651
652// ImportStatusFlags describes the type of ImportStatus.Status. The C API in gpgme.h simply uses "unsigned".
653type ImportStatusFlags uint
654
655const (
656ImportNew ImportStatusFlags = C.GPGME_IMPORT_NEW
657ImportUID ImportStatusFlags = C.GPGME_IMPORT_UID
658ImportSIG ImportStatusFlags = C.GPGME_IMPORT_SIG
659ImportSubKey ImportStatusFlags = C.GPGME_IMPORT_SUBKEY
660ImportSecret ImportStatusFlags = C.GPGME_IMPORT_SECRET
661)
662
663type ImportStatus struct {
664Fingerprint string
665Result error
666Status ImportStatusFlags
667}
668
669type ImportResult struct {
670Considered int
671NoUserID int
672Imported int
673ImportedRSA int
674Unchanged int
675NewUserIDs int
676NewSubKeys int
677NewSignatures int
678NewRevocations int
679SecretRead int
680SecretImported int
681SecretUnchanged int
682NotImported int
683Imports []ImportStatus
684}
685
686func (c *Context) Import(keyData *Data) (*ImportResult, error) {
687err := handleError(C.gpgme_op_import(c.ctx, keyData.dh))
688runtime.KeepAlive(c)
689runtime.KeepAlive(keyData)
690if err != nil {
691return nil, err
692}
693res := C.gpgme_op_import_result(c.ctx)
694runtime.KeepAlive(c)
695// NOTE: c must be live as long as we are accessing res.
696imports := []ImportStatus{}
697for s := res.imports; s != nil; s = s.next {
698imports = append(imports, ImportStatus{
699Fingerprint: C.GoString(s.fpr),
700Result: handleError(s.result),
701Status: ImportStatusFlags(s.status),
702})
703}
704importResult := &ImportResult{
705Considered: int(res.considered),
706NoUserID: int(res.no_user_id),
707Imported: int(res.imported),
708ImportedRSA: int(res.imported_rsa),
709Unchanged: int(res.unchanged),
710NewUserIDs: int(res.new_user_ids),
711NewSubKeys: int(res.new_sub_keys),
712NewSignatures: int(res.new_signatures),
713NewRevocations: int(res.new_revocations),
714SecretRead: int(res.secret_read),
715SecretImported: int(res.secret_imported),
716SecretUnchanged: int(res.secret_unchanged),
717NotImported: int(res.not_imported),
718Imports: imports,
719}
720runtime.KeepAlive(c) // for all accesses to res above
721return importResult, nil
722}
723
724type Key struct {
725k C.gpgme_key_t // WARNING: Call Runtime.KeepAlive(k) after ANY passing of k.k to C
726}
727
728func newKey() *Key {
729k := &Key{}
730runtime.SetFinalizer(k, (*Key).Release)
731return k
732}
733
734func (k *Key) Release() {
735C.gpgme_key_release(k.k)
736runtime.KeepAlive(k)
737k.k = nil
738}
739
740func (k *Key) Revoked() bool {
741res := C.key_revoked(k.k) != 0
742runtime.KeepAlive(k)
743return res
744}
745
746func (k *Key) Expired() bool {
747res := C.key_expired(k.k) != 0
748runtime.KeepAlive(k)
749return res
750}
751
752func (k *Key) Disabled() bool {
753res := C.key_disabled(k.k) != 0
754runtime.KeepAlive(k)
755return res
756}
757
758func (k *Key) Invalid() bool {
759res := C.key_invalid(k.k) != 0
760runtime.KeepAlive(k)
761return res
762}
763
764func (k *Key) CanEncrypt() bool {
765res := C.key_can_encrypt(k.k) != 0
766runtime.KeepAlive(k)
767return res
768}
769
770func (k *Key) CanSign() bool {
771res := C.key_can_sign(k.k) != 0
772runtime.KeepAlive(k)
773return res
774}
775
776func (k *Key) CanCertify() bool {
777res := C.key_can_certify(k.k) != 0
778runtime.KeepAlive(k)
779return res
780}
781
782func (k *Key) Secret() bool {
783res := C.key_secret(k.k) != 0
784runtime.KeepAlive(k)
785return res
786}
787
788func (k *Key) CanAuthenticate() bool {
789res := C.key_can_authenticate(k.k) != 0
790runtime.KeepAlive(k)
791return res
792}
793
794func (k *Key) IsQualified() bool {
795res := C.key_is_qualified(k.k) != 0
796runtime.KeepAlive(k)
797return res
798}
799
800func (k *Key) Protocol() Protocol {
801res := Protocol(k.k.protocol)
802runtime.KeepAlive(k)
803return res
804}
805
806func (k *Key) IssuerSerial() string {
807res := C.GoString(k.k.issuer_serial)
808runtime.KeepAlive(k)
809return res
810}
811
812func (k *Key) IssuerName() string {
813res := C.GoString(k.k.issuer_name)
814runtime.KeepAlive(k)
815return res
816}
817
818func (k *Key) ChainID() string {
819res := C.GoString(k.k.chain_id)
820runtime.KeepAlive(k)
821return res
822}
823
824func (k *Key) OwnerTrust() Validity {
825res := Validity(k.k.owner_trust)
826runtime.KeepAlive(k)
827return res
828}
829
830func (k *Key) SubKeys() *SubKey {
831subKeys := k.k.subkeys
832runtime.KeepAlive(k)
833if subKeys == nil {
834return nil
835}
836return &SubKey{k: subKeys, parent: k} // The parent: k reference ensures subKeys remains valid
837}
838
839func (k *Key) UserIDs() *UserID {
840uids := k.k.uids
841runtime.KeepAlive(k)
842if uids == nil {
843return nil
844}
845return &UserID{u: uids, parent: k} // The parent: k reference ensures uids remains valid
846}
847
848func (k *Key) KeyListMode() KeyListMode {
849res := KeyListMode(k.k.keylist_mode)
850runtime.KeepAlive(k)
851return res
852}
853
854type SubKey struct {
855k C.gpgme_subkey_t
856parent *Key // make sure the key is not released when we have a reference to a subkey
857}
858
859func (k *SubKey) Next() *SubKey {
860if k.k.next == nil {
861return nil
862}
863return &SubKey{k: k.k.next, parent: k.parent}
864}
865
866func (k *SubKey) Revoked() bool {
867return C.subkey_revoked(k.k) != 0
868}
869
870func (k *SubKey) Expired() bool {
871return C.subkey_expired(k.k) != 0
872}
873
874func (k *SubKey) Disabled() bool {
875return C.subkey_disabled(k.k) != 0
876}
877
878func (k *SubKey) Invalid() bool {
879return C.subkey_invalid(k.k) != 0
880}
881
882func (k *SubKey) Secret() bool {
883return C.subkey_secret(k.k) != 0
884}
885
886func (k *SubKey) KeyID() string {
887return C.GoString(k.k.keyid)
888}
889
890func (k *SubKey) Fingerprint() string {
891return C.GoString(k.k.fpr)
892}
893
894func (k *SubKey) Created() time.Time {
895if k.k.timestamp <= 0 {
896return time.Time{}
897}
898return time.Unix(int64(k.k.timestamp), 0)
899}
900
901func (k *SubKey) Expires() time.Time {
902if k.k.expires <= 0 {
903return time.Time{}
904}
905return time.Unix(int64(k.k.expires), 0)
906}
907
908func (k *SubKey) CardNumber() string {
909return C.GoString(k.k.card_number)
910}
911
912type UserID struct {
913u C.gpgme_user_id_t
914parent *Key // make sure the key is not released when we have a reference to a user ID
915}
916
917func (u *UserID) Next() *UserID {
918if u.u.next == nil {
919return nil
920}
921return &UserID{u: u.u.next, parent: u.parent}
922}
923
924func (u *UserID) Revoked() bool {
925return C.uid_revoked(u.u) != 0
926}
927
928func (u *UserID) Invalid() bool {
929return C.uid_invalid(u.u) != 0
930}
931
932func (u *UserID) Validity() Validity {
933return Validity(u.u.validity)
934}
935
936func (u *UserID) UID() string {
937return C.GoString(u.u.uid)
938}
939
940func (u *UserID) Name() string {
941return C.GoString(u.u.name)
942}
943
944func (u *UserID) Comment() string {
945return C.GoString(u.u.comment)
946}
947
948func (u *UserID) Email() string {
949return C.GoString(u.u.email)
950}
951