mirror of
https://github.com/containers/skopeo.git
synced 2025-05-08 07:56:18 +00:00
Add signing and verification to the signature package
This commit is contained in:
parent
9595b3336f
commit
69d5a131c9
Dockerfile
hack
signature
docker.godocker_test.go
fixtures
.gitignorecorrupt.signaturedouble.signatureexpired.signatureimage.manifest.jsonimage.signatureinfo.goinvalid-blob.signaturepubring.gpgsecring.gpgtrustdb.gpgunsigned-encrypted.signatureunsigned-literal.signature
mechanism.gomechanism_test.gosignature.gosignature_test.govendor/github.com/mtrmac/gpgme
@ -1,6 +1,8 @@
|
||||
FROM fedora
|
||||
|
||||
RUN dnf -y update && dnf install -y make git golang golang-github-cpuguy83-go-md2man \
|
||||
# gpgme bindings deps
|
||||
libassuan-devel gpgme-devel \
|
||||
# registry v1 deps
|
||||
xz-devel \
|
||||
python-devel \
|
||||
|
@ -14,6 +14,7 @@ clone git github.com/pmezard/go-difflib master
|
||||
clone git github.com/docker/docker master
|
||||
clone git github.com/docker/distribution master
|
||||
clone git github.com/opencontainers/runc master
|
||||
clone git github.com/mtrmac/gpgme master
|
||||
|
||||
clean
|
||||
|
||||
|
42
signature/docker.go
Normal file
42
signature/docker.go
Normal file
@ -0,0 +1,42 @@
|
||||
// Note: Consider the API unstable until the code supports at least three different image formats or transports.
|
||||
|
||||
package signature
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func dockerManifestDigest(manifest []byte) string {
|
||||
hash := sha256.Sum256(manifest)
|
||||
return "sha256:" + hex.EncodeToString(hash[:])
|
||||
}
|
||||
|
||||
// SignDockerManifest returns a signature for manifest as the specified dockerReference,
|
||||
// using mech and keyIdentity.
|
||||
func SignDockerManifest(manifest []byte, dockerReference string, mech SigningMechanism, keyIdentity string) ([]byte, error) {
|
||||
manifestDigest := dockerManifestDigest(manifest)
|
||||
sig := privateSignature{
|
||||
Signature{
|
||||
DockerManifestDigest: manifestDigest,
|
||||
DockerReference: dockerReference,
|
||||
},
|
||||
}
|
||||
return sig.sign(mech, keyIdentity)
|
||||
}
|
||||
|
||||
// VerifyDockerManifestSignature checks that unverifiedSignature uses expectedKeyIdentity to sign unverifiedManifest as expectedDockerReference,
|
||||
// using mech.
|
||||
func VerifyDockerManifestSignature(unverifiedSignature, unverifiedManifest []byte,
|
||||
expectedDockerReference string, mech SigningMechanism, expectedKeyIdentity string) (*Signature, error) {
|
||||
expectedManifestDigest := dockerManifestDigest(unverifiedManifest)
|
||||
sig, err := verifyAndExtractSignature(mech, unverifiedSignature, expectedKeyIdentity, expectedDockerReference)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if sig.DockerManifestDigest != expectedManifestDigest {
|
||||
return nil, InvalidSignatureError{msg: fmt.Sprintf("Docker manifest digest %s does not match %s", sig.DockerManifestDigest, expectedManifestDigest)}
|
||||
}
|
||||
return sig, nil
|
||||
}
|
77
signature/docker_test.go
Normal file
77
signature/docker_test.go
Normal file
@ -0,0 +1,77 @@
|
||||
package signature
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/projectatomic/skopeo/signature/fixtures"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestDockerManifestDigest(t *testing.T) {
|
||||
manifest, err := ioutil.ReadFile("fixtures/image.manifest.json")
|
||||
require.NoError(t, err)
|
||||
digest := dockerManifestDigest(manifest)
|
||||
assert.Equal(t, fixtures.TestImageManifestDigest, digest)
|
||||
|
||||
digest = dockerManifestDigest([]byte{})
|
||||
assert.Equal(t, "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", digest)
|
||||
}
|
||||
|
||||
func TestSignDockerManifest(t *testing.T) {
|
||||
mech, err := newGPGSigningMechanismInDirectory(testGPGHomeDirectory)
|
||||
require.NoError(t, err)
|
||||
manifest, err := ioutil.ReadFile("fixtures/image.manifest.json")
|
||||
require.NoError(t, err)
|
||||
|
||||
// Successful signing
|
||||
signature, err := SignDockerManifest(manifest, fixtures.TestImageSignatureReference, mech, fixtures.TestKeyFingerprint)
|
||||
require.NoError(t, err)
|
||||
|
||||
verified, err := VerifyDockerManifestSignature(signature, manifest, fixtures.TestImageSignatureReference, mech, fixtures.TestKeyFingerprint)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, fixtures.TestImageSignatureReference, verified.DockerReference)
|
||||
assert.Equal(t, fixtures.TestImageManifestDigest, verified.DockerManifestDigest)
|
||||
|
||||
// Error creating blob to sign
|
||||
_, err = SignDockerManifest(manifest, "", mech, fixtures.TestKeyFingerprint)
|
||||
assert.Error(t, err)
|
||||
|
||||
// Error signing
|
||||
_, err = SignDockerManifest(manifest, fixtures.TestImageSignatureReference, mech, "this fingerprint doesn't exist")
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestVerifyDockerManifestSignature(t *testing.T) {
|
||||
mech, err := newGPGSigningMechanismInDirectory(testGPGHomeDirectory)
|
||||
require.NoError(t, err)
|
||||
manifest, err := ioutil.ReadFile("fixtures/image.manifest.json")
|
||||
require.NoError(t, err)
|
||||
signature, err := ioutil.ReadFile("fixtures/image.signature")
|
||||
require.NoError(t, err)
|
||||
|
||||
// Successful verification
|
||||
sig, err := VerifyDockerManifestSignature(signature, manifest, fixtures.TestImageSignatureReference, mech, fixtures.TestKeyFingerprint)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, fixtures.TestImageSignatureReference, sig.DockerReference)
|
||||
assert.Equal(t, fixtures.TestImageManifestDigest, sig.DockerManifestDigest)
|
||||
|
||||
// For extra paranoia, test that we return nil data on error.
|
||||
|
||||
// Error verifying signature
|
||||
corruptSignature, err := ioutil.ReadFile("fixtures/corrupt.signature")
|
||||
sig, err = VerifyDockerManifestSignature(corruptSignature, manifest, fixtures.TestImageSignatureReference, mech, fixtures.TestKeyFingerprint)
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, sig)
|
||||
|
||||
// Key fingerprint mismatch
|
||||
sig, err = VerifyDockerManifestSignature(signature, manifest, fixtures.TestImageSignatureReference, mech, "unexpected fingerprint")
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, sig)
|
||||
|
||||
// Docker manifest digest mismatch
|
||||
sig, err = VerifyDockerManifestSignature(signature, []byte("unexpected manifest"), fixtures.TestImageSignatureReference, mech, fixtures.TestKeyFingerprint)
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, sig)
|
||||
}
|
4
signature/fixtures/.gitignore
vendored
Normal file
4
signature/fixtures/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
/*.gpg~
|
||||
/.gpg-v21-migrated
|
||||
/private-keys-v1.d
|
||||
/random_seed
|
BIN
signature/fixtures/corrupt.signature
Normal file
BIN
signature/fixtures/corrupt.signature
Normal file
Binary file not shown.
BIN
signature/fixtures/double.signature
Normal file
BIN
signature/fixtures/double.signature
Normal file
Binary file not shown.
BIN
signature/fixtures/expired.signature
Normal file
BIN
signature/fixtures/expired.signature
Normal file
Binary file not shown.
26
signature/fixtures/image.manifest.json
Normal file
26
signature/fixtures/image.manifest.json
Normal file
@ -0,0 +1,26 @@
|
||||
{
|
||||
"schemaVersion": 2,
|
||||
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
|
||||
"config": {
|
||||
"mediaType": "application/vnd.docker.container.image.v1+json",
|
||||
"size": 7023,
|
||||
"digest": "sha256:b5b2b2c507a0944348e0303114d8d93aaaa081732b86451d9bce1f432a537bc7"
|
||||
},
|
||||
"layers": [
|
||||
{
|
||||
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
|
||||
"size": 32654,
|
||||
"digest": "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f"
|
||||
},
|
||||
{
|
||||
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
|
||||
"size": 16724,
|
||||
"digest": "sha256:3c3a4604a545cdc127456d94e421cd355bca5b528f4a9c1905b15da2eb4a4c6b"
|
||||
},
|
||||
{
|
||||
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
|
||||
"size": 73109,
|
||||
"digest": "sha256:ec4b8955958665577945c89419d1af06b5f7636b4ac3da7f12184802ad867736"
|
||||
}
|
||||
]
|
||||
}
|
BIN
signature/fixtures/image.signature
Normal file
BIN
signature/fixtures/image.signature
Normal file
Binary file not shown.
10
signature/fixtures/info.go
Normal file
10
signature/fixtures/info.go
Normal file
@ -0,0 +1,10 @@
|
||||
package fixtures
|
||||
|
||||
const (
|
||||
// TestImageManifestDigest is the Docker manifest digest of "image.manifest.json"
|
||||
TestImageManifestDigest = "sha256:20bf21ed457b390829cdbeec8795a7bea1626991fda603e0d01b4e7f60427e55"
|
||||
// TestImageSignatureReference is the Docker image reference signed in "image.signature"
|
||||
TestImageSignatureReference = "testing/manifest"
|
||||
// TestKeyFingerprint is the fingerprint of the private key in this directory.
|
||||
TestKeyFingerprint = "1D8230F6CDB6A06716E414C1DB72F2188BB46CC8"
|
||||
)
|
BIN
signature/fixtures/invalid-blob.signature
Normal file
BIN
signature/fixtures/invalid-blob.signature
Normal file
Binary file not shown.
BIN
signature/fixtures/pubring.gpg
Normal file
BIN
signature/fixtures/pubring.gpg
Normal file
Binary file not shown.
BIN
signature/fixtures/secring.gpg
Normal file
BIN
signature/fixtures/secring.gpg
Normal file
Binary file not shown.
BIN
signature/fixtures/trustdb.gpg
Normal file
BIN
signature/fixtures/trustdb.gpg
Normal file
Binary file not shown.
BIN
signature/fixtures/unsigned-encrypted.signature
Normal file
BIN
signature/fixtures/unsigned-encrypted.signature
Normal file
Binary file not shown.
BIN
signature/fixtures/unsigned-literal.signature
Normal file
BIN
signature/fixtures/unsigned-literal.signature
Normal file
Binary file not shown.
96
signature/mechanism.go
Normal file
96
signature/mechanism.go
Normal file
@ -0,0 +1,96 @@
|
||||
package signature
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"github.com/mtrmac/gpgme"
|
||||
)
|
||||
|
||||
// SigningMechanism abstracts a way to sign binary blobs and verify their signatures.
|
||||
// FIXME: Eventually expand on keyIdentity (namespace them between mechanisms to
|
||||
// eliminate ambiguities, support CA signatures and perhaps other key properties)
|
||||
type SigningMechanism interface {
|
||||
// Sign creates a (non-detached) signature of input using keyidentity
|
||||
Sign(input []byte, keyIdentity string) ([]byte, error)
|
||||
// Verify parses unverifiedSignature and returns the content and the signer's identity
|
||||
Verify(unverifiedSignature []byte) (contents []byte, keyIdentity string, err error)
|
||||
}
|
||||
|
||||
// A GPG/OpenPGP signing mechanism.
|
||||
type gpgSigningMechanism struct {
|
||||
ctx *gpgme.Context
|
||||
}
|
||||
|
||||
// NewGPGSigningMechanism returns a new GPG/OpenPGP signing mechanism.
|
||||
func NewGPGSigningMechanism() (SigningMechanism, error) {
|
||||
return newGPGSigningMechanismInDirectory("")
|
||||
}
|
||||
|
||||
// newGPGSigningMechanismInDirectory returns a new GPG/OpenPGP signing mechanism, using optionalDir if not empty.
|
||||
func newGPGSigningMechanismInDirectory(optionalDir string) (SigningMechanism, error) {
|
||||
ctx, err := gpgme.New()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = ctx.SetProtocol(gpgme.ProtocolOpenPGP); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if optionalDir != "" {
|
||||
err := ctx.SetEngineInfo(gpgme.ProtocolOpenPGP, "", optionalDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
ctx.SetArmor(false)
|
||||
ctx.SetTextMode(false)
|
||||
return gpgSigningMechanism{ctx: ctx}, nil
|
||||
}
|
||||
|
||||
// Sign implements SigningMechanism.Sign
|
||||
func (m gpgSigningMechanism) Sign(input []byte, keyIdentity string) ([]byte, error) {
|
||||
key, err := m.ctx.GetKey(keyIdentity, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
inputData, err := gpgme.NewDataBytes(input)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var sigBuffer bytes.Buffer
|
||||
sigData, err := gpgme.NewDataWriter(&sigBuffer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = m.ctx.Sign([]*gpgme.Key{key}, inputData, sigData, gpgme.SigModeNormal); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return sigBuffer.Bytes(), nil
|
||||
}
|
||||
|
||||
// Verify implements SigningMechanism.Verify
|
||||
func (m gpgSigningMechanism) Verify(unverifiedSignature []byte) (contents []byte, keyIdentity string, err error) {
|
||||
signedBuffer := bytes.Buffer{}
|
||||
signedData, err := gpgme.NewDataWriter(&signedBuffer)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
unverifiedSignatureData, err := gpgme.NewDataBytes(unverifiedSignature)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
_, sigs, err := m.ctx.Verify(unverifiedSignatureData, nil, signedData)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
if len(sigs) != 1 {
|
||||
return nil, "", InvalidSignatureError{msg: fmt.Sprintf("Unexpected GPG signature count %d", len(sigs))}
|
||||
}
|
||||
sig := sigs[0]
|
||||
// This is sig.Summary == gpgme.SigSumValid except for key trust, which we handle ourselves
|
||||
if sig.Status != nil || sig.Validity == gpgme.ValidityNever || sig.ValidityReason != nil || sig.WrongKeyUsage {
|
||||
// FIXME: Better error reporting eventually
|
||||
return nil, "", InvalidSignatureError{msg: fmt.Sprintf("Invalid GPG signature: %#v", sig)}
|
||||
}
|
||||
return signedBuffer.Bytes(), sig.Fingerprint, nil
|
||||
}
|
102
signature/mechanism_test.go
Normal file
102
signature/mechanism_test.go
Normal file
@ -0,0 +1,102 @@
|
||||
package signature
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/projectatomic/skopeo/signature/fixtures"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
const (
|
||||
testGPGHomeDirectory = "./fixtures"
|
||||
)
|
||||
|
||||
func TestNewGPGSigningMechanism(t *testing.T) {
|
||||
// A dumb test just for code coverage. We test more with newGPGSigningMechanismInDirectory().
|
||||
_, err := NewGPGSigningMechanism()
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestNewGPGSigningMechanismInDirectory(t *testing.T) {
|
||||
// A dumb test just for code coverage.
|
||||
_, err := newGPGSigningMechanismInDirectory(testGPGHomeDirectory)
|
||||
assert.NoError(t, err)
|
||||
// The various GPG failure cases are not obviously easy to reach.
|
||||
}
|
||||
|
||||
func TestGPGSigningMechanismSign(t *testing.T) {
|
||||
mech, err := newGPGSigningMechanismInDirectory(testGPGHomeDirectory)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Successful signing
|
||||
content := []byte("content")
|
||||
signature, err := mech.Sign(content, fixtures.TestKeyFingerprint)
|
||||
require.NoError(t, err)
|
||||
|
||||
signedContent, signingFingerprint, err := mech.Verify(signature)
|
||||
require.NoError(t, err)
|
||||
assert.EqualValues(t, content, signedContent)
|
||||
assert.Equal(t, fixtures.TestKeyFingerprint, signingFingerprint)
|
||||
|
||||
// Error signing
|
||||
_, err = mech.Sign(content, "this fingerprint doesn't exist")
|
||||
assert.Error(t, err)
|
||||
// The various GPG/GPGME failures cases are not obviously easy to reach.
|
||||
}
|
||||
|
||||
func assertSigningError(t *testing.T, content []byte, fingerprint string, err error) {
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, content)
|
||||
assert.Empty(t, fingerprint)
|
||||
}
|
||||
|
||||
func TestGPGSigningMechanismVerify(t *testing.T) {
|
||||
mech, err := newGPGSigningMechanismInDirectory(testGPGHomeDirectory)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Successful verification
|
||||
signature, err := ioutil.ReadFile("./fixtures/invalid-blob.signature")
|
||||
require.NoError(t, err)
|
||||
content, signingFingerprint, err := mech.Verify(signature)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []byte("This is not JSON\n"), content)
|
||||
assert.Equal(t, fixtures.TestKeyFingerprint, signingFingerprint)
|
||||
|
||||
// For extra paranoia, test that we return nil data on error.
|
||||
|
||||
// Completely invalid signature.
|
||||
content, signingFingerprint, err = mech.Verify([]byte{})
|
||||
assertSigningError(t, content, signingFingerprint, err)
|
||||
|
||||
content, signingFingerprint, err = mech.Verify([]byte("invalid signature"))
|
||||
assertSigningError(t, content, signingFingerprint, err)
|
||||
|
||||
// Literal packet, not a signature
|
||||
signature, err = ioutil.ReadFile("./fixtures/unsigned-literal.signature")
|
||||
require.NoError(t, err)
|
||||
content, signingFingerprint, err = mech.Verify(signature)
|
||||
assertSigningError(t, content, signingFingerprint, err)
|
||||
|
||||
// Encrypted data, not a signature.
|
||||
signature, err = ioutil.ReadFile("./fixtures/unsigned-encrypted.signature")
|
||||
require.NoError(t, err)
|
||||
content, signingFingerprint, err = mech.Verify(signature)
|
||||
assertSigningError(t, content, signingFingerprint, err)
|
||||
|
||||
// FIXME? Is there a way to create a multi-signature so that gpgme_op_verify returns multiple signatures?
|
||||
|
||||
// Expired signature
|
||||
signature, err = ioutil.ReadFile("./fixtures/expired.signature")
|
||||
require.NoError(t, err)
|
||||
content, signingFingerprint, err = mech.Verify(signature)
|
||||
assertSigningError(t, content, signingFingerprint, err)
|
||||
|
||||
// Corrupt signature
|
||||
signature, err = ioutil.ReadFile("./fixtures/corrupt.signature")
|
||||
require.NoError(t, err)
|
||||
content, signingFingerprint, err = mech.Verify(signature)
|
||||
assertSigningError(t, content, signingFingerprint, err)
|
||||
// The various GPG/GPGME failures cases are not obviously easy to reach.
|
||||
}
|
@ -167,3 +167,38 @@ func (s *privateSignature) UnmarshalJSON(data []byte) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Sign formats the signature and returns a blob signed using mech and keyIdentity
|
||||
func (s privateSignature) sign(mech SigningMechanism, keyIdentity string) ([]byte, error) {
|
||||
json, err := json.Marshal(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return mech.Sign(json, keyIdentity)
|
||||
}
|
||||
|
||||
// verifyAndExtractSignature verifies that signature has been signed by expectedKeyIdentity
|
||||
// using mech for expectedDockerReference, and returns it (without matching its contents to an image).
|
||||
func verifyAndExtractSignature(mech SigningMechanism, unverifiedSignature []byte,
|
||||
expectedKeyIdentity, expectedDockerReference string) (*Signature, error) {
|
||||
signed, keyIdentity, err := mech.Verify(unverifiedSignature)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if keyIdentity != expectedKeyIdentity {
|
||||
return nil, InvalidSignatureError{msg: fmt.Sprintf("Signature by %s does not match expected fingerprint %s", keyIdentity, expectedKeyIdentity)}
|
||||
}
|
||||
|
||||
var unmatchedSignature privateSignature
|
||||
if err := json.Unmarshal(signed, &unmatchedSignature); err != nil {
|
||||
return nil, InvalidSignatureError{msg: err.Error()}
|
||||
}
|
||||
|
||||
if unmatchedSignature.DockerReference != expectedDockerReference {
|
||||
return nil, InvalidSignatureError{msg: fmt.Sprintf("Docker reference %s does not match %s",
|
||||
unmatchedSignature.DockerReference, expectedDockerReference)}
|
||||
}
|
||||
signature := unmatchedSignature.Signature // Policy OK.
|
||||
return &signature, nil
|
||||
}
|
||||
|
@ -2,8 +2,10 @@ package signature
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/projectatomic/skopeo/signature/fixtures"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
@ -200,3 +202,78 @@ func TestUnmarshalJSON(t *testing.T) {
|
||||
assert.Equal(t, validSig, s)
|
||||
}
|
||||
}
|
||||
|
||||
type savedEnvironment struct {
|
||||
vars []string
|
||||
}
|
||||
|
||||
func TestSign(t *testing.T) {
|
||||
mech, err := newGPGSigningMechanismInDirectory(testGPGHomeDirectory)
|
||||
require.NoError(t, err)
|
||||
|
||||
sig := privateSignature{
|
||||
Signature{
|
||||
DockerManifestDigest: "digest!@#",
|
||||
DockerReference: "reference#@!",
|
||||
},
|
||||
}
|
||||
|
||||
// Successful signing
|
||||
signature, err := sig.sign(mech, fixtures.TestKeyFingerprint)
|
||||
require.NoError(t, err)
|
||||
|
||||
verified, err := verifyAndExtractSignature(mech, signature, fixtures.TestKeyFingerprint, sig.DockerReference)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, sig.Signature, *verified)
|
||||
|
||||
// Error creating blob to sign
|
||||
_, err = privateSignature{}.sign(mech, fixtures.TestKeyFingerprint)
|
||||
assert.Error(t, err)
|
||||
|
||||
// Error signing
|
||||
_, err = sig.sign(mech, "this fingerprint doesn't exist")
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestVerifyAndExtractSignature(t *testing.T) {
|
||||
mech, err := newGPGSigningMechanismInDirectory(testGPGHomeDirectory)
|
||||
require.NoError(t, err)
|
||||
|
||||
signature, err := ioutil.ReadFile("./fixtures/image.signature")
|
||||
require.NoError(t, err)
|
||||
|
||||
// Successful verification
|
||||
sig, err := verifyAndExtractSignature(mech, signature, fixtures.TestKeyFingerprint, fixtures.TestImageSignatureReference)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, fixtures.TestImageSignatureReference, sig.DockerReference)
|
||||
assert.Equal(t, fixtures.TestImageManifestDigest, sig.DockerManifestDigest)
|
||||
|
||||
// For extra paranoia, test that we return a nil signature object on error.
|
||||
|
||||
// Completely invalid signature.
|
||||
sig, err = verifyAndExtractSignature(mech, []byte{}, fixtures.TestKeyFingerprint, fixtures.TestImageSignatureReference)
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, sig)
|
||||
|
||||
sig, err = verifyAndExtractSignature(mech, []byte("invalid signature"), fixtures.TestKeyFingerprint, fixtures.TestImageSignatureReference)
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, sig)
|
||||
|
||||
// Valid signature of non-JSON
|
||||
invalidBlobSignature, err := ioutil.ReadFile("./fixtures/invalid-blob.signature")
|
||||
require.NoError(t, err)
|
||||
sig, err = verifyAndExtractSignature(mech, invalidBlobSignature, fixtures.TestKeyFingerprint, fixtures.TestImageSignatureReference)
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, sig)
|
||||
|
||||
// Valid signature with a wrong key
|
||||
sig, err = verifyAndExtractSignature(mech, signature, "unexpected fingerprint", fixtures.TestImageSignatureReference)
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, sig)
|
||||
|
||||
// Valid signature with a wrong image reference
|
||||
sig, err = verifyAndExtractSignature(mech, signature, fixtures.TestKeyFingerprint, "unexpected docker reference")
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, sig)
|
||||
}
|
||||
|
1
vendor/github.com/mtrmac/gpgme/.gitignore
generated
vendored
Normal file
1
vendor/github.com/mtrmac/gpgme/.gitignore
generated
vendored
Normal file
@ -0,0 +1 @@
|
||||
testdata/gpghome/random_seed
|
12
vendor/github.com/mtrmac/gpgme/LICENSE
generated
vendored
Normal file
12
vendor/github.com/mtrmac/gpgme/LICENSE
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
Copyright (c) 2015, James Fargher <proglottis@gmail.com>
|
||||
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.
|
13
vendor/github.com/mtrmac/gpgme/README.md
generated
vendored
Normal file
13
vendor/github.com/mtrmac/gpgme/README.md
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
# 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)
|
42
vendor/github.com/mtrmac/gpgme/callbacks.go
generated
vendored
Normal file
42
vendor/github.com/mtrmac/gpgme/callbacks.go
generated
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
package gpgme
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
var callbacks struct {
|
||||
sync.Mutex
|
||||
m map[int]interface{}
|
||||
c int
|
||||
}
|
||||
|
||||
func callbackAdd(v interface{}) int {
|
||||
callbacks.Lock()
|
||||
defer callbacks.Unlock()
|
||||
if callbacks.m == nil {
|
||||
callbacks.m = make(map[int]interface{})
|
||||
}
|
||||
callbacks.c++
|
||||
ret := callbacks.c
|
||||
callbacks.m[ret] = v
|
||||
return ret
|
||||
}
|
||||
|
||||
func callbackLookup(c int) interface{} {
|
||||
callbacks.Lock()
|
||||
defer callbacks.Unlock()
|
||||
ret := callbacks.m[c]
|
||||
if ret == nil {
|
||||
panic("callback pointer not found")
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func callbackDelete(c int) {
|
||||
callbacks.Lock()
|
||||
defer callbacks.Unlock()
|
||||
if callbacks.m[c] == nil {
|
||||
panic("callback pointer not found")
|
||||
}
|
||||
delete(callbacks.m, c)
|
||||
}
|
191
vendor/github.com/mtrmac/gpgme/data.go
generated
vendored
Normal file
191
vendor/github.com/mtrmac/gpgme/data.go
generated
vendored
Normal file
@ -0,0 +1,191 @@
|
||||
package gpgme
|
||||
|
||||
// #include <string.h>
|
||||
// #include <gpgme.h>
|
||||
// #include <errno.h>
|
||||
// #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(*(*int)(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(*(*int)(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.off_t, whence C.int) C.off_t {
|
||||
d := callbackLookup(*(*int)(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.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 int
|
||||
}
|
||||
|
||||
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.gpgme_data_new_from_cbs(&d.dh, &d.cbs, unsafe.Pointer(&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.gpgme_data_new_from_cbs(&d.dh, &d.cbs, unsafe.Pointer(&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.gpgme_data_new_from_cbs(&d.dh, &d.cbs, unsafe.Pointer(&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.gpgme_data_new_from_cbs(&d.dh, &d.cbs, unsafe.Pointer(&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.gpgme_data_seek(d.dh, C.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))
|
||||
}
|
81
vendor/github.com/mtrmac/gpgme/go_gpgme.c
generated
vendored
Normal file
81
vendor/github.com/mtrmac/gpgme/go_gpgme.c
generated
vendored
Normal file
@ -0,0 +1,81 @@
|
||||
#include "go_gpgme.h"
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
extern unsigned int subkey_revoked(gpgme_subkey_t k) {
|
||||
return k->revoked;
|
||||
}
|
||||
|
||||
extern unsigned int subkey_expired(gpgme_subkey_t k) {
|
||||
return k->expired;
|
||||
}
|
||||
|
||||
extern unsigned int subkey_disabled(gpgme_subkey_t k) {
|
||||
return k->disabled;
|
||||
}
|
||||
|
||||
extern unsigned int subkey_invalid(gpgme_subkey_t k) {
|
||||
return k->invalid;
|
||||
}
|
||||
|
||||
extern 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;
|
||||
}
|
32
vendor/github.com/mtrmac/gpgme/go_gpgme.h
generated
vendored
Normal file
32
vendor/github.com/mtrmac/gpgme/go_gpgme.h
generated
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
#ifndef GO_GPGME_H
|
||||
#define GO_GPGME_H
|
||||
|
||||
#include <gpgme.h>
|
||||
|
||||
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 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
|
671
vendor/github.com/mtrmac/gpgme/gpgme.go
generated
vendored
Normal file
671
vendor/github.com/mtrmac/gpgme/gpgme.go
generated
vendored
Normal file
@ -0,0 +1,671 @@
|
||||
// Package gpgme provides a Go wrapper for the GPGME library
|
||||
package gpgme
|
||||
|
||||
// #cgo LDFLAGS: -lgpgme -lassuan -lgpg-error
|
||||
// #include <stdlib.h>
|
||||
// #include <gpgme.h>
|
||||
// #include "go_gpgme.h"
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"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(*(*int)(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
|
||||
// ProtocolSpawn Protocol = C.GPGME_PROTOCOL_SPAWN // Unavailable in 1.4.3
|
||||
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
|
||||
// EncryptNoCompress EncryptFlag = C.GPGME_ENCRYPT_NO_COMPRESS // Unavailable in 1.4.3
|
||||
)
|
||||
|
||||
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
|
||||
// KeyListModeWithSecret KeyListMode = C.GPGME_KEYLIST_MODE_WITH_SECRET // Unavailable in 1.4.3
|
||||
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 int
|
||||
|
||||
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.gpgme_set_passphrase_cb(c.ctx, C.gpgme_passphrase_cb_t(C.gogpgme_passfunc), unsafe.Pointer(&cbc))
|
||||
} else {
|
||||
c.cbc = 0
|
||||
_, err = C.gpgme_set_passphrase_cb(c.ctx, nil, nil)
|
||||
}
|
||||
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))
|
||||
return key, handleError(C.gpgme_get_key(c.ctx, cfpr, &key.k, cbool(secret)))
|
||||
}
|
||||
|
||||
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 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)
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
Loading…
Reference in New Issue
Block a user