mirror of
https://github.com/containers/skopeo.git
synced 2025-07-16 15:52:24 +00:00
Add SigningMechanism.ImportKeysFromBytes
This will be needed for verification against specified public keys. Also rerun hack/vendor.sh to pick up import support from github.com/mtrmac/gpgme .
This commit is contained in:
parent
721a628f4a
commit
aee0abb5d2
19
signature/fixtures/public-key.gpg
Normal file
19
signature/fixtures/public-key.gpg
Normal file
@ -0,0 +1,19 @@
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
Version: GnuPG v1
|
||||
|
||||
mI0EVurzqQEEAL3qkFq4K2URtSWVDYnQUNA9HdM9sqS2eAWfqUFMrkD5f+oN+LBL
|
||||
tPyaE5GNLA0vXY7nHAM2TeM8ijZ/eMP17Raj64JL8GhCymL3wn2jNvb9XaF0R0s6
|
||||
H0IaRPPu45A3SnxLwm4Orc/9Z7/UxtYjKSg9xOaTiVPzJgaf5Vm4J4ApABEBAAG0
|
||||
EnNrb3BlbyB0ZXN0aW5nIGtleYi4BBMBAgAiBQJW6vOpAhsDBgsJCAcDAgYVCAIJ
|
||||
CgsEFgIDAQIeAQIXgAAKCRDbcvIYi7RsyBbOBACgJFiKDlQ1UyvsNmGqJ7D0OpbS
|
||||
1OppJlradKgZXyfahFswhFI+7ZREvELLHbinq3dBy5cLXRWzQKdJZNHknSN5Tjf2
|
||||
0ipVBQuqpcBo+dnKiG4zH6fhTri7yeTZksIDfsqlI6FXDOdKLUSnahagEBn4yU+x
|
||||
jHPvZk5SuuZv56A45biNBFbq86kBBADIC/9CsAlOmRALuYUmkhcqEjuFwn3wKz2d
|
||||
IBjzgvro7zcVNNCgxQfMEjcUsvEh5cx13G3QQHcwOKy3M6Bv6VMhfZjd+1P1el4P
|
||||
0fJS8GFmhWRBknMN8jFsgyohQeouQ798RFFv94KszfStNnr/ae8oao5URmoUXSCa
|
||||
/MdUxn0YKwARAQABiJ8EGAECAAkFAlbq86kCGwwACgkQ23LyGIu0bMjUywQAq0dn
|
||||
lUpDNSoLTcpNWuVvHQ7c/qmnE4TyiSLiRiAywdEWA6gMiyhUUucuGsEhMFP1WX1k
|
||||
UNwArZ6UG7BDOUsvngP7jKGNqyUOQrq1s/r8D+0MrJGOWErGLlfttO2WeoijECkI
|
||||
5qm8cXzAra3Xf/Z3VjxYTKSnNu37LtZkakdTdYE=
|
||||
=tJAt
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
@ -13,6 +13,10 @@ import (
|
||||
// FIXME: Eventually expand on keyIdentity (namespace them between mechanisms to
|
||||
// eliminate ambiguities, support CA signatures and perhaps other key properties)
|
||||
type SigningMechanism interface {
|
||||
// ImportKeysFromBytes imports public keys from the supplied blob and returns their identities.
|
||||
// The blob is assumed to have an appropriate format (the caller is expected to know which one).
|
||||
// NOTE: This may modify long-term state (e.g. key storage in a directory underlying the mechanism).
|
||||
ImportKeysFromBytes(blob []byte) ([]string, error)
|
||||
// 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
|
||||
@ -49,6 +53,25 @@ func newGPGSigningMechanismInDirectory(optionalDir string) (SigningMechanism, er
|
||||
return gpgSigningMechanism{ctx: ctx}, nil
|
||||
}
|
||||
|
||||
// ImportKeysFromBytes implements SigningMechanism.ImportKeysFromBytes
|
||||
func (m gpgSigningMechanism) ImportKeysFromBytes(blob []byte) ([]string, error) {
|
||||
inputData, err := gpgme.NewDataBytes(blob)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res, err := m.ctx.Import(inputData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
keyIdentities := []string{}
|
||||
for _, i := range res.Imports {
|
||||
if i.Result == nil {
|
||||
keyIdentities = append(keyIdentities, i.Fingerprint)
|
||||
}
|
||||
}
|
||||
return keyIdentities, nil
|
||||
}
|
||||
|
||||
// Sign implements SigningMechanism.Sign
|
||||
func (m gpgSigningMechanism) Sign(input []byte, keyIdentity string) ([]byte, error) {
|
||||
key, err := m.ctx.GetKey(keyIdentity, true)
|
||||
|
@ -1,7 +1,9 @@
|
||||
package signature
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@ -25,6 +27,45 @@ func TestNewGPGSigningMechanismInDirectory(t *testing.T) {
|
||||
// The various GPG failure cases are not obviously easy to reach.
|
||||
}
|
||||
|
||||
func TestGPGSigningMechanismImportKeysFromBytes(t *testing.T) {
|
||||
testDir, err := ioutil.TempDir("", "gpg-import-keys")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(testDir)
|
||||
|
||||
mech, err := newGPGSigningMechanismInDirectory(testDir)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Try validating a signature when the key is unknown.
|
||||
signature, err := ioutil.ReadFile("./fixtures/invalid-blob.signature")
|
||||
require.NoError(t, err)
|
||||
content, signingFingerprint, err := mech.Verify(signature)
|
||||
require.Error(t, err)
|
||||
|
||||
// Successful import
|
||||
keyBlob, err := ioutil.ReadFile("./fixtures/public-key.gpg")
|
||||
require.NoError(t, err)
|
||||
keyIdentities, err := mech.ImportKeysFromBytes(keyBlob)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []string{TestKeyFingerprint}, keyIdentities)
|
||||
|
||||
// After import, the signature should validate.
|
||||
content, signingFingerprint, err = mech.Verify(signature)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []byte("This is not JSON\n"), content)
|
||||
assert.Equal(t, TestKeyFingerprint, signingFingerprint)
|
||||
|
||||
// Two keys: just concatenate the valid input twice.
|
||||
keyIdentities, err = mech.ImportKeysFromBytes(bytes.Join([][]byte{keyBlob, keyBlob}, nil))
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []string{TestKeyFingerprint, TestKeyFingerprint}, keyIdentities)
|
||||
|
||||
// Invalid input: This is accepted anyway by GPG, just returns no keys.
|
||||
keyIdentities, err = mech.ImportKeysFromBytes([]byte("This is invalid"))
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []string{}, keyIdentities)
|
||||
// The various GPG/GPGME failures cases are not obviously easy to reach.
|
||||
}
|
||||
|
||||
func TestGPGSigningMechanismSign(t *testing.T) {
|
||||
mech, err := newGPGSigningMechanismInDirectory(testGPGHomeDirectory)
|
||||
require.NoError(t, err)
|
||||
|
66
vendor/github.com/mtrmac/gpgme/gpgme.go
generated
vendored
66
vendor/github.com/mtrmac/gpgme/gpgme.go
generated
vendored
@ -472,6 +472,72 @@ func (c *Context) Sign(signers []*Key, plain, sig *Data, mode SigMode) error {
|
||||
return handleError(C.gpgme_op_sign(c.ctx, plain.dh, sig.dh, C.gpgme_sig_mode_t(mode)))
|
||||
}
|
||||
|
||||
// ImportStatusFlags describes the type of ImportStatus.Status. The C API in gpgme.h simply uses "unsigned".
|
||||
type ImportStatusFlags uint
|
||||
|
||||
const (
|
||||
ImportNew ImportStatusFlags = C.GPGME_IMPORT_NEW
|
||||
ImportUID ImportStatusFlags = C.GPGME_IMPORT_UID
|
||||
ImportSIG ImportStatusFlags = C.GPGME_IMPORT_SIG
|
||||
ImportSubKey ImportStatusFlags = C.GPGME_IMPORT_SUBKEY
|
||||
ImportSecret ImportStatusFlags = C.GPGME_IMPORT_SECRET
|
||||
)
|
||||
|
||||
type ImportStatus struct {
|
||||
Fingerprint string
|
||||
Result error
|
||||
Status ImportStatusFlags
|
||||
}
|
||||
|
||||
type ImportResult struct {
|
||||
Considered int
|
||||
NoUserID int
|
||||
Imported int
|
||||
ImportedRSA int
|
||||
Unchanged int
|
||||
NewUserIDs int
|
||||
NewSubKeys int
|
||||
NewSignatures int
|
||||
NewRevocations int
|
||||
SecretRead int
|
||||
SecretImported int
|
||||
SecretUnchanged int
|
||||
NotImported int
|
||||
Imports []ImportStatus
|
||||
}
|
||||
|
||||
func (c *Context) Import(keyData *Data) (*ImportResult, error) {
|
||||
err := handleError(C.gpgme_op_import(c.ctx, keyData.dh))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res := C.gpgme_op_import_result(c.ctx)
|
||||
imports := []ImportStatus{}
|
||||
for s := res.imports; s != nil; s = s.next {
|
||||
imports = append(imports, ImportStatus{
|
||||
Fingerprint: C.GoString(s.fpr),
|
||||
Result: handleError(s.result),
|
||||
Status: ImportStatusFlags(s.status),
|
||||
})
|
||||
}
|
||||
return &ImportResult{
|
||||
Considered: int(res.considered),
|
||||
NoUserID: int(res.no_user_id),
|
||||
Imported: int(res.imported),
|
||||
ImportedRSA: int(res.imported_rsa),
|
||||
Unchanged: int(res.unchanged),
|
||||
NewUserIDs: int(res.new_user_ids),
|
||||
NewSubKeys: int(res.new_sub_keys),
|
||||
NewSignatures: int(res.new_signatures),
|
||||
NewRevocations: int(res.new_revocations),
|
||||
SecretRead: int(res.secret_read),
|
||||
SecretImported: int(res.secret_imported),
|
||||
SecretUnchanged: int(res.secret_unchanged),
|
||||
NotImported: int(res.not_imported),
|
||||
Imports: imports,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type Key struct {
|
||||
k C.gpgme_key_t
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user