Detailed changes
@@ -3,10 +3,10 @@ package commands
import (
"fmt"
+ "github.com/spf13/cobra"
+
"github.com/MichaelMure/git-bug/cache"
- "github.com/MichaelMure/git-bug/identity"
"github.com/MichaelMure/git-bug/util/interrupt"
- "github.com/spf13/cobra"
)
func ResolveUser(repo *cache.RepoCache, args []string) (*cache.IdentityCache, []string, error) {
@@ -39,11 +39,7 @@ func runKey(cmd *cobra.Command, args []string) error {
}
for _, key := range id.Keys() {
- pubkey, err := key.GetPublicKey()
- if err != nil {
- return err
- }
- fmt.Println(identity.EncodeKeyFingerprint(pubkey.Fingerprint))
+ fmt.Println(key.Fingerprint())
}
return nil
@@ -13,8 +13,8 @@ import (
)
var (
- keyAddArmoredFile string
- keyAddArmored string
+ keyAddArmoredFile string
+ keyAddArmored string
)
func runKeyAdd(cmd *cobra.Command, args []string) error {
@@ -52,7 +52,7 @@ func runKeyAdd(cmd *cobra.Command, args []string) error {
}
}
- key, err := identity.NewKey(keyAddArmored)
+ key, err := identity.NewKeyFromArmored(keyAddArmored)
if err != nil {
return err
}
@@ -61,9 +61,9 @@ func runKeyAdd(cmd *cobra.Command, args []string) error {
if err != nil {
return errors.Wrap(err, "failed to create validator")
}
- commitHash := validator.KeyCommitHash(key.PublicKey.KeyId)
+ commitHash := validator.KeyCommitHash(key.publicKey().KeyId)
if commitHash != "" {
- return fmt.Errorf("key id %d is already used by the key introduced in commit %s", key.PublicKey.KeyId, commitHash)
+ return fmt.Errorf("key id %d is already used by the key introduced in commit %s", key.publicKey.KeyId, commitHash)
}
err = id.Mutate(func(mutator identity.Mutator) identity.Mutator {
@@ -3,11 +3,12 @@ package commands
import (
"fmt"
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+
"github.com/MichaelMure/git-bug/cache"
"github.com/MichaelMure/git-bug/identity"
"github.com/MichaelMure/git-bug/util/interrupt"
- "github.com/pkg/errors"
- "github.com/spf13/cobra"
)
func runKeyRm(cmd *cobra.Command, args []string) error {
@@ -22,7 +23,7 @@ func runKeyRm(cmd *cobra.Command, args []string) error {
return errors.New("missing key fingerprint")
}
- keyFingerprint := args[0]
+ fingerprint := args[0]
args = args[1:]
id, args, err := ResolveUser(backend, args)
@@ -34,21 +35,10 @@ func runKeyRm(cmd *cobra.Command, args []string) error {
return fmt.Errorf("unexpected arguments: %s", args)
}
- fingerprint, err := identity.DecodeKeyFingerprint(keyFingerprint)
- if err != nil {
- return errors.Wrap(err, "invalid key fingerprint")
- }
-
var removedKey *identity.Key
err = id.Mutate(func(mutator identity.Mutator) identity.Mutator {
for j, key := range mutator.Keys {
- pubkey, err := key.GetPublicKey()
- if err != nil {
- fmt.Printf("Warning: failed to decode public key: %s", err)
- continue
- }
-
- if pubkey.Fingerprint == fingerprint {
+ if key.Fingerprint() == fingerprint {
removedKey = key
copy(mutator.Keys[j:], mutator.Keys[j+1:])
mutator.Keys = mutator.Keys[:len(mutator.Keys)-1]
@@ -24,7 +24,7 @@ func runValidate(cmd *cobra.Command, args []string) error {
return err
}
- fmt.Printf("first commit signed with key: %s\n", identity.EncodeKeyFingerprint(validator.FirstKey.PublicKey.Fingerprint))
+ fmt.Printf("first commit signed with key: %s\n", identity.encodeKeyFingerprint(validator.FirstKey.publicKey.Fingerprint))
var refErr error
for _, ref := range args {
@@ -45,7 +45,7 @@ func TestIdentityCommitLoad(t *testing.T) {
name: "René Descartes",
email: "rene.descartes@example.com",
keys: []*Key{
- {ArmoredPublicKey: repository.CreatePubkey(t)},
+ {armoredPublicKey: repository.CreatePubkey(t)},
},
},
{
@@ -53,7 +53,7 @@ func TestIdentityCommitLoad(t *testing.T) {
name: "René Descartes",
email: "rene.descartes@example.com",
keys: []*Key{
- {ArmoredPublicKey: repository.CreatePubkey(t)},
+ {armoredPublicKey: repository.CreatePubkey(t)},
},
},
{
@@ -61,7 +61,7 @@ func TestIdentityCommitLoad(t *testing.T) {
name: "René Descartes",
email: "rene.descartes@example.com",
keys: []*Key{
- {ArmoredPublicKey: repository.CreatePubkey(t)},
+ {armoredPublicKey: repository.CreatePubkey(t)},
},
},
},
@@ -90,7 +90,7 @@ func TestIdentityCommitLoad(t *testing.T) {
name: "René Descartes",
email: "rene.descartes@example.com",
keys: []*Key{
- {ArmoredPublicKey: repository.CreatePubkey(t)},
+ {armoredPublicKey: repository.CreatePubkey(t)},
},
})
@@ -99,7 +99,7 @@ func TestIdentityCommitLoad(t *testing.T) {
name: "René Descartes",
email: "rene.descartes@example.com",
keys: []*Key{
- {ArmoredPublicKey: repository.CreatePubkey(t)},
+ {armoredPublicKey: repository.CreatePubkey(t)},
},
})
@@ -118,7 +118,7 @@ func TestIdentityCommitLoad(t *testing.T) {
func loadKeys(identity *Identity) {
for _, v := range identity.versions {
for _, k := range v.keys {
- k.GetPublicKey()
+ k.publicKey()
}
}
}
@@ -139,7 +139,7 @@ func TestIdentity_ValidKeysAtTime(t *testing.T) {
name: "René Descartes",
email: "rene.descartes@example.com",
keys: []*Key{
- {ArmoredPublicKey: "pubkeyA"},
+ {armoredPublicKey: "pubkeyA"},
},
},
{
@@ -147,7 +147,7 @@ func TestIdentity_ValidKeysAtTime(t *testing.T) {
name: "René Descartes",
email: "rene.descartes@example.com",
keys: []*Key{
- {ArmoredPublicKey: "pubkeyB"},
+ {armoredPublicKey: "pubkeyB"},
},
},
{
@@ -155,7 +155,7 @@ func TestIdentity_ValidKeysAtTime(t *testing.T) {
name: "René Descartes",
email: "rene.descartes@example.com",
keys: []*Key{
- {ArmoredPublicKey: "pubkeyC"},
+ {armoredPublicKey: "pubkeyC"},
},
},
{
@@ -163,7 +163,7 @@ func TestIdentity_ValidKeysAtTime(t *testing.T) {
name: "René Descartes",
email: "rene.descartes@example.com",
keys: []*Key{
- {ArmoredPublicKey: "pubkeyD"},
+ {armoredPublicKey: "pubkeyD"},
},
},
{
@@ -171,20 +171,20 @@ func TestIdentity_ValidKeysAtTime(t *testing.T) {
name: "René Descartes",
email: "rene.descartes@example.com",
keys: []*Key{
- {ArmoredPublicKey: "pubkeyE"},
+ {armoredPublicKey: "pubkeyE"},
},
},
},
}
assert.Nil(t, identity.ValidKeysAtTime(10))
- assert.Equal(t, identity.ValidKeysAtTime(100), []*Key{{ArmoredPublicKey: "pubkeyA"}})
- assert.Equal(t, identity.ValidKeysAtTime(140), []*Key{{ArmoredPublicKey: "pubkeyA"}})
- assert.Equal(t, identity.ValidKeysAtTime(200), []*Key{{ArmoredPublicKey: "pubkeyB"}})
- assert.Equal(t, identity.ValidKeysAtTime(201), []*Key{{ArmoredPublicKey: "pubkeyD"}})
- assert.Equal(t, identity.ValidKeysAtTime(202), []*Key{{ArmoredPublicKey: "pubkeyD"}})
- assert.Equal(t, identity.ValidKeysAtTime(300), []*Key{{ArmoredPublicKey: "pubkeyE"}})
- assert.Equal(t, identity.ValidKeysAtTime(3000), []*Key{{ArmoredPublicKey: "pubkeyE"}})
+ assert.Equal(t, identity.ValidKeysAtTime(100), []*Key{{armoredPublicKey: "pubkeyA"}})
+ assert.Equal(t, identity.ValidKeysAtTime(140), []*Key{{armoredPublicKey: "pubkeyA"}})
+ assert.Equal(t, identity.ValidKeysAtTime(200), []*Key{{armoredPublicKey: "pubkeyB"}})
+ assert.Equal(t, identity.ValidKeysAtTime(201), []*Key{{armoredPublicKey: "pubkeyD"}})
+ assert.Equal(t, identity.ValidKeysAtTime(202), []*Key{{armoredPublicKey: "pubkeyD"}})
+ assert.Equal(t, identity.ValidKeysAtTime(300), []*Key{{armoredPublicKey: "pubkeyE"}})
+ assert.Equal(t, identity.ValidKeysAtTime(3000), []*Key{{armoredPublicKey: "pubkeyE"}})
}
// Test the immutable or mutable metadata search
@@ -2,6 +2,7 @@ package identity
import (
"encoding/hex"
+ "encoding/json"
"fmt"
"strings"
@@ -10,14 +11,20 @@ import (
"golang.org/x/crypto/openpgp/packet"
)
+// Key hold a cryptographic public key
type Key struct {
// PubKey is the armored PGP public key.
- ArmoredPublicKey string `json:"pub_key"`
+ armoredPublicKey string
- PublicKey *packet.PublicKey `json:"-"`
+ // memoized decoded public key
+ publicKey *packet.PublicKey
}
-func NewKey(armoredPGPKey string) (*Key, error) {
+type keyJSON struct {
+ ArmoredPublicKey string `json:"armored_pub_key"`
+}
+
+func NewKeyFromArmored(armoredPGPKey string) (*Key, error) {
publicKey, err := parsePublicKey(armoredPGPKey)
if err != nil {
return nil, err
@@ -26,10 +33,70 @@ func NewKey(armoredPGPKey string) (*Key, error) {
return &Key{armoredPGPKey, publicKey}, nil
}
+func (k *Key) MarshalJSON() ([]byte, error) {
+ return json.Marshal(keyJSON{
+ ArmoredPublicKey: k.armoredPublicKey,
+ })
+}
+
+func (k *Key) UnmarshalJSON(data []byte) error {
+ var aux keyJSON
+
+ if err := json.Unmarshal(data, &aux); err != nil {
+ return err
+ }
+
+ k.armoredPublicKey = aux.ArmoredPublicKey
+
+ return nil
+}
+
+func (k *Key) Validate() error {
+ if k.publicKey != nil {
+ return nil
+ }
+
+ publicKey, err := parsePublicKey(k.armoredPublicKey)
+ if err != nil {
+ return errors.Wrap(err, "invalid public key")
+ }
+ k.publicKey = publicKey
+
+ return nil
+}
+
+func (k *Key) Clone() *Key {
+ clone := *k
+ return &clone
+}
+
+func (k *Key) publicKey() *packet.PublicKey {
+ if k.publicKey != nil {
+ return k.publicKey
+ }
+
+ publicKey, err := parsePublicKey(k.armoredPublicKey)
+ if err != nil {
+ // Coding problem, a key should be validated before use
+ panic("invalid key: " + err.Error())
+ }
+
+ k.publicKey = publicKey
+ return k.publicKey
+}
+
+func (k Key) Armored() string {
+ return k.armoredPublicKey
+}
+
+func (k Key) Fingerprint() string {
+ return encodeKeyFingerprint(k.publicKey().Fingerprint)
+}
+
func parsePublicKey(armoredPublicKey string) (*packet.PublicKey, error) {
block, err := armor.Decode(strings.NewReader(armoredPublicKey))
if err != nil {
- return nil, errors.Wrap(err, "failed to dearmor public key")
+ return nil, errors.Wrap(err, "failed to decode armored public key")
}
reader := packet.NewReader(block.Body)
@@ -40,14 +107,14 @@ func parsePublicKey(armoredPublicKey string) (*packet.PublicKey, error) {
publicKey, ok := p.(*packet.PublicKey)
if !ok {
- return nil, errors.New("got no packet.PublicKey")
+ return nil, errors.New("got no packet.publicKey")
}
return publicKey, nil
}
-// DecodeKeyFingerprint decodes a 40 hex digits long fingerprint into bytes.
-func DecodeKeyFingerprint(keyFingerprint string) ([20]byte, error) {
+// decodeKeyFingerprint decodes a 40 hex digits long fingerprint into bytes.
+func decodeKeyFingerprint(keyFingerprint string) ([20]byte, error) {
var fingerprint [20]byte
fingerprintBytes, err := hex.DecodeString(keyFingerprint)
if err != nil {
@@ -60,24 +127,7 @@ func DecodeKeyFingerprint(keyFingerprint string) ([20]byte, error) {
return fingerprint, nil
}
-func EncodeKeyFingerprint(fingerprint [20]byte) string {
+// encodeKeyFingerprint encode a byte representation of a fingerprint into a 40 hex digits string.
+func encodeKeyFingerprint(fingerprint [20]byte) string {
return strings.ToUpper(hex.EncodeToString(fingerprint[:]))
}
-
-func (k *Key) Validate() error {
- _, err := k.GetPublicKey()
- return err
-}
-
-func (k *Key) Clone() *Key {
- clone := *k
- return &clone
-}
-
-func (k *Key) GetPublicKey() (*packet.PublicKey, error) {
- var err error
- if k.PublicKey == nil {
- k.PublicKey, err = parsePublicKey(k.ArmoredPublicKey)
- }
- return k.PublicKey, err
-}
@@ -1,9 +1,11 @@
package identity
import (
+ "encoding/json"
"strings"
"testing"
+ "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@@ -14,8 +16,45 @@ func TestDecodeKeyFingerprint(t *testing.T) {
}
func checkEncodeDecodeKeyFingerprint(t *testing.T, fingerprint string) {
- decoded, err := DecodeKeyFingerprint(fingerprint)
+ decoded, err := decodeKeyFingerprint(fingerprint)
require.NoError(t, err)
- require.Equal(t, fingerprint, EncodeKeyFingerprint(decoded))
+ require.Equal(t, fingerprint, encodeKeyFingerprint(decoded))
}
+func TestKeySerialize(t *testing.T) {
+ armored := `-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mI0EXrVFzwEEAL7roW5pBs7PYhnW8XdHuMBUrOqx+TR8JPsTLzlFFKHniJ7Cxm24
+rj+nCiVAC3yI5hEWbLYLp6HoSCAEJim+ac+LsoH0Rxz325l+EYz7nAq44rebfNuy
+A5LD9/KVzLAu0FO27pgCiH9RpsFVYveHYtR1jDDvag6MLdlTZaQfqCGnABEBAAG0
+LFJlbsOpIERlc2NhcnRlcyA8cmVuZS5kZXNjYXJ0ZXNAZXhhbXBsZS5jb20+iM4E
+EwEKADgWIQQpwni46BlhwjZt/3bXoSG7jO2rwwUCXrVFzwIbAwULCQgHAgYVCgkI
+CwIEFgIDAQIeAQIXgAAKCRDXoSG7jO2rw5LcBACPp+2cwUcYCiHZVUAnAHokY7R0
+msjA/YryCy+rcW86TcV7KuyZjg3wCHi7DrDuvYIDXr83W2XaCoJktAW/+aENj8QH
+6r7Tini3ENmNT8caqiCJGE0iY0QRXZomxAoZc5kvDq596ifoUA08ILncGla7Uq04
++3Da+JBLWoDQvVP3xbiNBF61Rc8BBADBYKVgB1eHgXOorCeKYLCDSNwklkkdCN5u
+WZygmr/EMpT7YGuAvW70WKHcd0zo+MX/3fWvJeTQDVmReNF0zJv0OSjcAsamcOQ9
+G9rdL3bKWMGJRtTeXmtZ6BkP4YU227VkFTFXvQzt8WD5CXGQJtEZRXQqHKNjNNIY
+JUxF6VfJtQARAQABiLYEGAEKACAWIQQpwni46BlhwjZt/3bXoSG7jO2rwwUCXrVF
+zwIbDAAKCRDXoSG7jO2rw7xEA/9TJD/M6vO160zNr7fCB31rqGUvkHWOKaeSHJmG
+AvFBrNiBG+nGRjc2XbZqSaykO7ApcmLzgh8zzlB3gxZjorbEGRoEUsYD5pmZhfFl
+kZyE/aXEbuTIXXcR9fyuDGvP2eF4RPth8P4ew9ycXl89IUdbapD3JKg/ptkgw8dy
+y8TVdw==
+=01qL
+-----END PGP PUBLIC KEY BLOCK-----`
+
+ before, err := NewKeyFromArmored(armored)
+ assert.NoError(t, err)
+ assert.NoError(t, before.Validate())
+
+ data, err := json.Marshal(before)
+ assert.NoError(t, err)
+
+ var after Key
+ err = json.Unmarshal(data, &after)
+ assert.NoError(t, err)
+ assert.NoError(t, after.Validate())
+
+ assert.NotEmpty(t, after.Fingerprint())
+ assert.Equal(t, before.Fingerprint(), after.Fingerprint())
+}
@@ -14,10 +14,10 @@ func TestVersionSerialize(t *testing.T) {
avatarURL: "avatarUrl",
keys: []*Key{
{
- ArmoredPublicKey: "pubkey1",
+ armoredPublicKey: "pubkey1",
},
{
- ArmoredPublicKey: "pubkey2",
+ armoredPublicKey: "pubkey2",
},
},
nonce: makeNonce(20),