aboutsummaryrefslogtreecommitdiffstats
path: root/identity
diff options
context:
space:
mode:
authorMichael Muré <batolettre@gmail.com>2021-01-03 23:59:25 +0100
committerMichael Muré <batolettre@gmail.com>2021-02-14 12:19:00 +0100
commit8d63c983c982f93cc48d3996d6bd097ddeeb327f (patch)
tree94d85594e11965f9780df53a5c0c2b2550c02184 /identity
parent4ef92efeb905102d37b81fafa0ac2173594ef30a (diff)
downloadgit-bug-8d63c983c982f93cc48d3996d6bd097ddeeb327f.tar.gz
WIP
Diffstat (limited to 'identity')
-rw-r--r--identity/identity.go11
-rw-r--r--identity/identity_actions_test.go2
-rw-r--r--identity/identity_stub.go4
-rw-r--r--identity/identity_test.go50
-rw-r--r--identity/interface.go3
-rw-r--r--identity/key.go187
-rw-r--r--identity/key_test.go21
-rw-r--r--identity/version_test.go20
8 files changed, 250 insertions, 48 deletions
diff --git a/identity/identity.go b/identity/identity.go
index ef488712..65019041 100644
--- a/identity/identity.go
+++ b/identity/identity.go
@@ -344,7 +344,7 @@ func (i *Identity) Commit(repo repository.ClockedRepo) error {
var commitHash repository.Hash
if lastCommit != "" {
- commitHash, err = repo.StoreCommitWithParent(treeHash, lastCommit)
+ commitHash, err = repo.StoreCommit(treeHash, lastCommit)
} else {
commitHash, err = repo.StoreCommit(treeHash)
}
@@ -518,6 +518,15 @@ func (i *Identity) Keys() []*Key {
return i.lastVersion().keys
}
+// SigningKey return the key that should be used to sign new messages. If no key is available, return nil.
+func (i *Identity) SigningKey() *Key {
+ keys := i.Keys()
+ if len(keys) > 0 {
+ return keys[0]
+ }
+ return nil
+}
+
// ValidKeysAtTime return the set of keys valid at a given lamport time
func (i *Identity) ValidKeysAtTime(clockName string, time lamport.Time) []*Key {
var result []*Key
diff --git a/identity/identity_actions_test.go b/identity/identity_actions_test.go
index 63f6aacd..54cb2a46 100644
--- a/identity/identity_actions_test.go
+++ b/identity/identity_actions_test.go
@@ -9,7 +9,7 @@ import (
)
func TestPushPull(t *testing.T) {
- repoA, repoB, remote := repository.SetupReposAndRemote()
+ repoA, repoB, remote := repository.SetupGoGitReposAndRemote()
defer repository.CleanupTestRepos(repoA, repoB, remote)
identity1, err := NewIdentity(repoA, "name1", "email1")
diff --git a/identity/identity_stub.go b/identity/identity_stub.go
index fec92010..91945378 100644
--- a/identity/identity_stub.go
+++ b/identity/identity_stub.go
@@ -71,6 +71,10 @@ func (IdentityStub) Keys() []*Key {
panic("identities needs to be properly loaded with identity.ReadLocal()")
}
+func (i *IdentityStub) SigningKey() *Key {
+ panic("identities needs to be properly loaded with identity.ReadLocal()")
+}
+
func (IdentityStub) ValidKeysAtTime(_ string, _ lamport.Time) []*Key {
panic("identities needs to be properly loaded with identity.ReadLocal()")
}
diff --git a/identity/identity_test.go b/identity/identity_test.go
index ad8317ce..2cdb4b36 100644
--- a/identity/identity_test.go
+++ b/identity/identity_test.go
@@ -36,18 +36,18 @@ func TestIdentityCommitLoad(t *testing.T) {
// multiple versions
- identity, err = NewIdentityFull(repo, "René Descartes", "rene.descartes@example.com", "", "", []*Key{{PubKey: "pubkeyA"}})
+ identity, err = NewIdentityFull(repo, "René Descartes", "rene.descartes@example.com", "", "", []*Key{generatePublicKey()})
require.NoError(t, err)
idBeforeCommit = identity.Id()
err = identity.Mutate(repo, func(orig *Mutator) {
- orig.Keys = []*Key{{PubKey: "pubkeyB"}}
+ orig.Keys = []*Key{generatePublicKey()}
})
require.NoError(t, err)
err = identity.Mutate(repo, func(orig *Mutator) {
- orig.Keys = []*Key{{PubKey: "pubkeyC"}}
+ orig.Keys = []*Key{generatePublicKey()}
})
require.NoError(t, err)
@@ -70,13 +70,13 @@ func TestIdentityCommitLoad(t *testing.T) {
err = identity.Mutate(repo, func(orig *Mutator) {
orig.Email = "rene@descartes.com"
- orig.Keys = []*Key{{PubKey: "pubkeyD"}}
+ orig.Keys = []*Key{generatePublicKey()}
})
require.NoError(t, err)
err = identity.Mutate(repo, func(orig *Mutator) {
orig.Email = "rene@descartes.com"
- orig.Keys = []*Key{{PubKey: "pubkeyD"}, {PubKey: "pubkeyE"}}
+ orig.Keys = []*Key{generatePublicKey(), generatePublicKey()}
})
require.NoError(t, err)
@@ -123,49 +123,45 @@ func commitsAreSet(t *testing.T, identity *Identity) {
// Test that the correct crypto keys are returned for a given lamport time
func TestIdentity_ValidKeysAtTime(t *testing.T) {
+ pubKeyA := generatePublicKey()
+ pubKeyB := generatePublicKey()
+ pubKeyC := generatePublicKey()
+ pubKeyD := generatePublicKey()
+ pubKeyE := generatePublicKey()
+
identity := Identity{
versions: []*version{
{
times: map[string]lamport.Time{"foo": 100},
- keys: []*Key{
- {PubKey: "pubkeyA"},
- },
+ keys: []*Key{pubKeyA},
},
{
times: map[string]lamport.Time{"foo": 200},
- keys: []*Key{
- {PubKey: "pubkeyB"},
- },
+ keys: []*Key{pubKeyB},
},
{
times: map[string]lamport.Time{"foo": 201},
- keys: []*Key{
- {PubKey: "pubkeyC"},
- },
+ keys: []*Key{pubKeyC},
},
{
times: map[string]lamport.Time{"foo": 201},
- keys: []*Key{
- {PubKey: "pubkeyD"},
- },
+ keys: []*Key{pubKeyD},
},
{
times: map[string]lamport.Time{"foo": 300},
- keys: []*Key{
- {PubKey: "pubkeyE"},
- },
+ keys: []*Key{pubKeyE},
},
},
}
require.Nil(t, identity.ValidKeysAtTime("foo", 10))
- require.Equal(t, identity.ValidKeysAtTime("foo", 100), []*Key{{PubKey: "pubkeyA"}})
- require.Equal(t, identity.ValidKeysAtTime("foo", 140), []*Key{{PubKey: "pubkeyA"}})
- require.Equal(t, identity.ValidKeysAtTime("foo", 200), []*Key{{PubKey: "pubkeyB"}})
- require.Equal(t, identity.ValidKeysAtTime("foo", 201), []*Key{{PubKey: "pubkeyD"}})
- require.Equal(t, identity.ValidKeysAtTime("foo", 202), []*Key{{PubKey: "pubkeyD"}})
- require.Equal(t, identity.ValidKeysAtTime("foo", 300), []*Key{{PubKey: "pubkeyE"}})
- require.Equal(t, identity.ValidKeysAtTime("foo", 3000), []*Key{{PubKey: "pubkeyE"}})
+ require.Equal(t, identity.ValidKeysAtTime("foo", 100), []*Key{pubKeyA})
+ require.Equal(t, identity.ValidKeysAtTime("foo", 140), []*Key{pubKeyA})
+ require.Equal(t, identity.ValidKeysAtTime("foo", 200), []*Key{pubKeyB})
+ require.Equal(t, identity.ValidKeysAtTime("foo", 201), []*Key{pubKeyD})
+ require.Equal(t, identity.ValidKeysAtTime("foo", 202), []*Key{pubKeyD})
+ require.Equal(t, identity.ValidKeysAtTime("foo", 300), []*Key{pubKeyE})
+ require.Equal(t, identity.ValidKeysAtTime("foo", 3000), []*Key{pubKeyE})
}
// Test the immutable or mutable metadata search
diff --git a/identity/interface.go b/identity/interface.go
index 92a03c51..528cb067 100644
--- a/identity/interface.go
+++ b/identity/interface.go
@@ -36,6 +36,9 @@ type Interface interface {
// Can be empty.
Keys() []*Key
+ // SigningKey return the key that should be used to sign new messages. If no key is available, return nil.
+ SigningKey() *Key
+
// ValidKeysAtTime return the set of keys valid at a given lamport time for a given clock of another entity
// Can be empty.
ValidKeysAtTime(clockName string, time lamport.Time) []*Key
diff --git a/identity/key.go b/identity/key.go
index cc948394..8dd5e8c1 100644
--- a/identity/key.go
+++ b/identity/key.go
@@ -1,18 +1,193 @@
package identity
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "io"
+ "strings"
+ "time"
+
+ "github.com/pkg/errors"
+ "golang.org/x/crypto/openpgp"
+ "golang.org/x/crypto/openpgp/armor"
+ "golang.org/x/crypto/openpgp/packet"
+
+ "github.com/MichaelMure/git-bug/repository"
+)
+
type Key struct {
- // The GPG fingerprint of the key
- Fingerprint string `json:"fingerprint"`
- PubKey string `json:"pub_key"`
+ public *packet.PublicKey
+ private *packet.PrivateKey
+}
+
+// GenerateKey generate a keypair (public+private)
+func GenerateKey() *Key {
+ entity, err := openpgp.NewEntity("", "", "", &packet.Config{
+ // The armored format doesn't include the creation time, which makes the round-trip data not being fully equal.
+ // We don't care about the creation time so we can set it to the zero value.
+ Time: func() time.Time {
+ return time.Time{}
+ },
+ })
+ if err != nil {
+ panic(err)
+ }
+ return &Key{
+ public: entity.PrimaryKey,
+ private: entity.PrivateKey,
+ }
+}
+
+// generatePublicKey generate only a public key (only useful for testing)
+// See GenerateKey for the details.
+func generatePublicKey() *Key {
+ k := GenerateKey()
+ k.private = nil
+ return k
+}
+
+func (k *Key) MarshalJSON() ([]byte, error) {
+ var buf bytes.Buffer
+ w, err := armor.Encode(&buf, openpgp.PublicKeyType, nil)
+ if err != nil {
+ return nil, err
+ }
+ err = k.public.Serialize(w)
+ if err != nil {
+ return nil, err
+ }
+ err = w.Close()
+ if err != nil {
+ return nil, err
+ }
+ return json.Marshal(buf.String())
+}
+
+func (k *Key) UnmarshalJSON(data []byte) error {
+ var armored string
+ err := json.Unmarshal(data, &armored)
+ if err != nil {
+ return err
+ }
+
+ block, err := armor.Decode(strings.NewReader(armored))
+ if err == io.EOF {
+ return fmt.Errorf("no armored data found")
+ }
+ if err != nil {
+ return err
+ }
+
+ if block.Type != openpgp.PublicKeyType {
+ return fmt.Errorf("invalid key type")
+ }
+
+ reader := packet.NewReader(block.Body)
+ p, err := reader.Next()
+ if err != nil {
+ return errors.Wrap(err, "failed to read public key packet")
+ }
+
+ public, ok := p.(*packet.PublicKey)
+ if !ok {
+ return errors.New("got no packet.publicKey")
+ }
+
+ // The armored format doesn't include the creation time, which makes the round-trip data not being fully equal.
+ // We don't care about the creation time so we can set it to the zero value.
+ public.CreationTime = time.Time{}
+
+ k.public = public
+ return nil
}
func (k *Key) Validate() error {
- // Todo
+ if k.public == nil {
+ return fmt.Errorf("nil public key")
+ }
+ if !k.public.CanSign() {
+ return fmt.Errorf("public key can't sign")
+ }
+
+ if k.private != nil {
+ if !k.private.CanSign() {
+ return fmt.Errorf("private key can't sign")
+ }
+ }
return nil
}
func (k *Key) Clone() *Key {
- clone := *k
- return &clone
+ clone := &Key{}
+
+ pub := *k.public
+ clone.public = &pub
+
+ if k.private != nil {
+ priv := *k.private
+ clone.private = &priv
+ }
+
+ return clone
+}
+
+func (k *Key) EnsurePrivateKey(repo repository.RepoKeyring) error {
+ if k.private != nil {
+ return nil
+ }
+
+ // item, err := repo.Keyring().Get(k.Fingerprint())
+ // if err != nil {
+ // return fmt.Errorf("no private key found for %s", k.Fingerprint())
+ // }
+ //
+
+ panic("TODO")
+}
+
+func (k *Key) Fingerprint() string {
+ return string(k.public.Fingerprint[:])
+}
+
+func (k *Key) PGPEntity() *openpgp.Entity {
+ return &openpgp.Entity{
+ PrimaryKey: k.public,
+ PrivateKey: k.private,
+ }
+}
+
+var _ openpgp.KeyRing = &PGPKeyring{}
+
+// PGPKeyring implement a openpgp.KeyRing from an slice of Key
+type PGPKeyring []*Key
+
+func (pk PGPKeyring) KeysById(id uint64) []openpgp.Key {
+ var result []openpgp.Key
+ for _, key := range pk {
+ if key.public.KeyId == id {
+ result = append(result, openpgp.Key{
+ PublicKey: key.public,
+ PrivateKey: key.private,
+ })
+ }
+ }
+ return result
+}
+
+func (pk PGPKeyring) KeysByIdUsage(id uint64, requiredUsage byte) []openpgp.Key {
+ // the only usage we care about is the ability to sign, which all keys should already be capable of
+ return pk.KeysById(id)
+}
+
+func (pk PGPKeyring) DecryptionKeys() []openpgp.Key {
+ result := make([]openpgp.Key, len(pk))
+ for i, key := range pk {
+ result[i] = openpgp.Key{
+ PublicKey: key.public,
+ PrivateKey: key.private,
+ }
+ }
+ return result
}
diff --git a/identity/key_test.go b/identity/key_test.go
new file mode 100644
index 00000000..3206c34e
--- /dev/null
+++ b/identity/key_test.go
@@ -0,0 +1,21 @@
+package identity
+
+import (
+ "encoding/json"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestKeyJSON(t *testing.T) {
+ k := generatePublicKey()
+
+ data, err := json.Marshal(k)
+ require.NoError(t, err)
+
+ var read Key
+ err = json.Unmarshal(data, &read)
+ require.NoError(t, err)
+
+ require.Equal(t, k, &read)
+}
diff --git a/identity/version_test.go b/identity/version_test.go
index 1efa0d03..385ad4d7 100644
--- a/identity/version_test.go
+++ b/identity/version_test.go
@@ -18,29 +18,23 @@ func makeIdentityTestRepo(t *testing.T) repository.ClockedRepo {
clock1, err := repo.GetOrCreateClock("foo")
require.NoError(t, err)
- err = clock1.Witness(42) // clock goes to 43
+ err = clock1.Witness(42)
require.NoError(t, err)
clock2, err := repo.GetOrCreateClock("bar")
require.NoError(t, err)
- err = clock2.Witness(34) // clock goes to 35
+ err = clock2.Witness(34)
require.NoError(t, err)
return repo
}
-func TestVersionSerialize(t *testing.T) {
+func TestVersionJSON(t *testing.T) {
repo := makeIdentityTestRepo(t)
keys := []*Key{
- {
- Fingerprint: "fingerprint1",
- PubKey: "pubkey1",
- },
- {
- Fingerprint: "fingerprint2",
- PubKey: "pubkey2",
- },
+ generatePublicKey(),
+ generatePublicKey(),
}
before, err := newVersion(repo, "name", "email", "login", "avatarUrl", keys)
@@ -57,8 +51,8 @@ func TestVersionSerialize(t *testing.T) {
avatarURL: "avatarUrl",
unixTime: time.Now().Unix(),
times: map[string]lamport.Time{
- "foo": 43,
- "bar": 35,
+ "foo": 42,
+ "bar": 34,
},
keys: keys,
nonce: before.nonce,