1
0
Fork 0
mirror of https://github.com/anyproto/any-sync.git synced 2025-06-08 05:57:03 +09:00

More key changes

This commit is contained in:
mcrakhman 2023-03-25 13:15:19 +01:00 committed by Mikhail Iudin
parent c00ea331dc
commit 3778465e3b
No known key found for this signature in database
GPG key ID: FAAAA8BAABDFF1C0
29 changed files with 229 additions and 1046 deletions

View file

@ -10,7 +10,7 @@ const CName = "common.accountservice"
type Service interface {
app.Component
Account() *accountdata.AccountData
Account() *accountdata.AccountKeys
}
type Config struct {

View file

@ -6,7 +6,7 @@ import (
"github.com/golang/mock/gomock"
)
func NewAccountServiceWithAccount(ctrl *gomock.Controller, acc *accountdata.AccountData) *MockService {
func NewAccountServiceWithAccount(ctrl *gomock.Controller, acc *accountdata.AccountKeys) *MockService {
mock := NewMockService(ctrl)
mock.EXPECT().Name().Return(accountservice.CName).AnyTimes()
mock.EXPECT().Init(gomock.Any()).AnyTimes()

View file

@ -36,10 +36,10 @@ func (m *MockService) EXPECT() *MockServiceMockRecorder {
}
// Account mocks base method.
func (m *MockService) Account() *accountdata.AccountData {
func (m *MockService) Account() *accountdata.AccountKeys {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Account")
ret0, _ := ret[0].(*accountdata.AccountData)
ret0, _ := ret[0].(*accountdata.AccountKeys)
return ret0
}

View file

@ -1,14 +1,11 @@
package accountdata
import (
"github.com/anytypeio/any-sync/util/keys/asymmetric/encryptionkey"
"github.com/anytypeio/any-sync/util/keys/asymmetric/signingkey"
"github.com/anytypeio/any-sync/util/crypto"
)
type AccountData struct { // TODO: create a convenient constructor for this
Identity []byte // public key
PeerKey signingkey.PrivKey
SignKey signingkey.PrivKey
EncKey encryptionkey.PrivKey
PeerId string
type AccountKeys struct {
PeerKey crypto.PrivKey
SignKey crypto.PrivKey
PeerId string
}

View file

@ -1,50 +1,43 @@
package list
import (
"github.com/anytypeio/any-sync/commonspace/object/accountdata"
"github.com/anytypeio/any-sync/commonspace/object/acl/aclrecordproto"
acllistbuilder "github.com/anytypeio/any-sync/commonspace/object/acl/testutils/acllistbuilder"
"github.com/anytypeio/any-sync/commonspace/object/keychain"
"github.com/anytypeio/any-sync/util/cidutil"
"github.com/anytypeio/any-sync/util/keys/asymmetric/signingkey"
"github.com/stretchr/testify/require"
"testing"
)
func TestAclRecordBuilder_BuildUserJoin(t *testing.T) {
st, err := acllistbuilder.NewListStorageWithTestName("userjoinexample.yml")
require.NoError(t, err, "building storage should not result in error")
testKeychain := st.(*acllistbuilder.AclListStorageBuilder).GetKeychain()
identity := testKeychain.GeneratedIdentities["D"]
signPrivKey := testKeychain.SigningKeysByYAMLName["D"]
encPrivKey := testKeychain.EncryptionKeysByYAMLName["D"]
acc := &accountdata.AccountData{
Identity: []byte(identity),
SignKey: signPrivKey,
EncKey: encPrivKey,
}
aclList, err := BuildAclListWithIdentity(acc, st)
require.NoError(t, err, "building acl list should be without error")
recordBuilder := newAclRecordBuilder(aclList.Id(), keychain.NewKeychain())
rk, err := testKeychain.GetKey("key.Read.EncKey").(*acllistbuilder.SymKey).Key.Raw()
require.NoError(t, err)
privKey, err := testKeychain.GetKey("key.Sign.Onetime1").(signingkey.PrivKey).Raw()
require.NoError(t, err)
userJoin, err := recordBuilder.BuildUserJoin(privKey, rk, aclList.AclState())
require.NoError(t, err)
marshalledJoin, err := userJoin.Marshal()
require.NoError(t, err)
id, err := cidutil.NewCidFromBytes(marshalledJoin)
require.NoError(t, err)
rawRec := &aclrecordproto.RawAclRecordWithId{
Payload: marshalledJoin,
Id: id,
}
res, err := aclList.AddRawRecord(rawRec)
require.True(t, res)
require.NoError(t, err)
require.Equal(t, aclrecordproto.AclUserPermissions_Writer, aclList.AclState().UserStates()[identity].Permissions)
//st, err := acllistbuilder2.NewListStorageWithTestName("userjoinexample.yml")
//require.NoError(t, err, "building storage should not result in error")
//
//testKeychain := st.(*acllistbuilder2.AclListStorageBuilder).GetKeychain()
//identity := testKeychain.GeneratedIdentities["D"]
//signPrivKey := testKeychain.SigningKeysByYAMLName["D"]
//encPrivKey := testKeychain.EncryptionKeysByYAMLName["D"]
//acc := &accountdata.AccountKeys{
// Identity: []byte(identity),
// PrivKey: signPrivKey,
// EncKey: encPrivKey,
//}
//
//aclList, err := BuildAclListWithIdentity(acc, st)
//require.NoError(t, err, "building acl list should be without error")
//recordBuilder := newAclRecordBuilder(aclList.Id(), keychain.NewKeychain())
//rk, err := testKeychain.GetKey("key.Read.EncKey").(*acllistbuilder2.SymKey).Key.Raw()
//require.NoError(t, err)
//privKey, err := testKeychain.GetKey("key.Sign.Onetime1").(signingkey.PrivKey).Raw()
//require.NoError(t, err)
//
//userJoin, err := recordBuilder.BuildUserJoin(privKey, rk, aclList.AclState())
//require.NoError(t, err)
//marshalledJoin, err := userJoin.Marshal()
//require.NoError(t, err)
//id, err := cidutil.NewCidFromBytes(marshalledJoin)
//require.NoError(t, err)
//rawRec := &aclrecordproto.RawAclRecordWithId{
// Payload: marshalledJoin,
// Id: id,
//}
//res, err := aclList.AddRawRecord(rawRec)
//require.True(t, res)
//require.NoError(t, err)
//require.Equal(t, aclrecordproto.AclUserPermissions_Writer, aclList.AclState().UserStates()[identity].Permissions)
}

View file

@ -2,20 +2,17 @@ package list
import (
"github.com/anytypeio/any-sync/commonspace/object/accountdata"
"github.com/anytypeio/any-sync/util/keys/asymmetric/encryptionkey"
"github.com/anytypeio/any-sync/util/keys/asymmetric/signingkey"
"github.com/anytypeio/any-sync/util/crypto"
)
type aclStateBuilder struct {
signPrivKey signingkey.PrivKey
encPrivKey encryptionkey.PrivKey
id string
privKey crypto.PrivKey
id string
}
func newAclStateBuilderWithIdentity(accountData *accountdata.AccountData) *aclStateBuilder {
func newAclStateBuilderWithIdentity(keys *accountdata.AccountKeys) *aclStateBuilder {
return &aclStateBuilder{
signPrivKey: accountData.SignKey,
encPrivKey: accountData.EncKey,
privKey: keys.SignKey,
}
}
@ -28,8 +25,8 @@ func (sb *aclStateBuilder) Init(id string) {
}
func (sb *aclStateBuilder) Build(records []*AclRecord) (state *AclState, err error) {
if sb.encPrivKey != nil && sb.signPrivKey != nil {
state, err = newAclStateWithKeys(sb.id, sb.signPrivKey, sb.encPrivKey)
if sb.privKey != nil {
state, err = newAclStateWithKeys(sb.id, sb.privKey)
if err != nil {
return
}

View file

@ -8,7 +8,6 @@ import (
"github.com/anytypeio/any-sync/commonspace/object/accountdata"
"github.com/anytypeio/any-sync/commonspace/object/acl/aclrecordproto"
"github.com/anytypeio/any-sync/commonspace/object/acl/liststorage"
"github.com/anytypeio/any-sync/commonspace/object/keychain"
"github.com/anytypeio/any-sync/util/crypto"
"sync"
)
@ -34,6 +33,7 @@ type AclList interface {
Get(id string) (*AclRecord, error)
Iterate(iterFunc IterFunc)
IterateFrom(startId string, iterFunc IterFunc)
KeyStorage() crypto.KeyStorage
AddRawRecord(rawRec *aclrecordproto.RawAclRecordWithId) (added bool, err error)
@ -48,23 +48,25 @@ type aclList struct {
stateBuilder *aclStateBuilder
recordBuilder AclRecordBuilder
keyStorage crypto.KeyStorage
aclState *AclState
keychain *keychain.Keychain
storage liststorage.ListStorage
sync.RWMutex
}
func BuildAclListWithIdentity(acc *accountdata.AccountData, storage liststorage.ListStorage) (AclList, error) {
func BuildAclListWithIdentity(acc *accountdata.AccountKeys, storage liststorage.ListStorage) (AclList, error) {
builder := newAclStateBuilderWithIdentity(acc)
return build(storage.Id(), builder, newAclRecordBuilder(storage.Id(), crypto.NewKeyStorage()), storage)
keyStorage := crypto.NewKeyStorage()
return build(storage.Id(), keyStorage, builder, newAclRecordBuilder(storage.Id(), keyStorage), storage)
}
func BuildAclList(storage liststorage.ListStorage) (AclList, error) {
return build(storage.Id(), newAclStateBuilder(), newAclRecordBuilder(storage.Id(), crypto.NewKeyStorage()), storage)
keyStorage := crypto.NewKeyStorage()
return build(storage.Id(), keyStorage, newAclStateBuilder(), newAclRecordBuilder(storage.Id(), crypto.NewKeyStorage()), storage)
}
func build(id string, stateBuilder *aclStateBuilder, recBuilder AclRecordBuilder, storage liststorage.ListStorage) (list AclList, err error) {
func build(id string, keyStorage crypto.KeyStorage, stateBuilder *aclStateBuilder, recBuilder AclRecordBuilder, storage liststorage.ListStorage) (list AclList, err error) {
head, err := storage.Head()
if err != nil {
return
@ -177,6 +179,10 @@ func (a *aclList) AclState() *AclState {
return a.aclState
}
func (a *aclList) KeyStorage() crypto.KeyStorage {
return a.keyStorage
}
func (a *aclList) IsAfter(first string, second string) (bool, error) {
firstRec, okFirst := a.indexes[first]
secondRec, okSecond := a.indexes[second]

View file

@ -1,91 +1,87 @@
package list
import (
"github.com/anytypeio/any-sync/commonspace/object/acl/aclrecordproto"
"github.com/anytypeio/any-sync/commonspace/object/acl/testutils/acllistbuilder"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"testing"
)
func TestAclList_AclState_UserInviteAndJoin(t *testing.T) {
st, err := acllistbuilder.NewListStorageWithTestName("userjoinexample.yml")
require.NoError(t, err, "building storage should not result in error")
keychain := st.(*acllistbuilder.AclListStorageBuilder).GetKeychain()
aclList, err := BuildAclList(st)
require.NoError(t, err, "building acl list should be without error")
idA := keychain.GetIdentity("A")
idB := keychain.GetIdentity("B")
idC := keychain.GetIdentity("C")
// checking final state
assert.Equal(t, aclrecordproto.AclUserPermissions_Admin, aclList.AclState().UserStates()[idA].Permissions)
assert.Equal(t, aclrecordproto.AclUserPermissions_Writer, aclList.AclState().UserStates()[idB].Permissions)
assert.Equal(t, aclrecordproto.AclUserPermissions_Reader, aclList.AclState().UserStates()[idC].Permissions)
assert.Equal(t, aclList.Head().CurrentReadKeyHash, aclList.AclState().CurrentReadKeyId())
var records []*AclRecord
aclList.Iterate(func(record *AclRecord) (IsContinue bool) {
records = append(records, record)
return true
})
// checking permissions at specific records
assert.Equal(t, 3, len(records))
_, err = aclList.AclState().StateAtRecord(records[1].Id, idB)
assert.Error(t, err, "B should have no permissions at record 1")
perm, err := aclList.AclState().StateAtRecord(records[2].Id, idB)
assert.NoError(t, err, "should have no error with permissions of B in the record 2")
assert.Equal(t, UserPermissionPair{
Identity: idB,
Permission: aclrecordproto.AclUserPermissions_Writer,
}, perm)
//st, err := acllistbuilder.NewListStorageWithTestName("userjoinexample.yml")
//require.NoError(t, err, "building storage should not result in error")
//
//keychain := st.(*acllistbuilder.AclListStorageBuilder).GetKeychain()
//
//aclList, err := BuildAclList(st)
//require.NoError(t, err, "building acl list should be without error")
//
//idA := keychain.GetIdentity("A")
//idB := keychain.GetIdentity("B")
//idC := keychain.GetIdentity("C")
//
//// checking final state
//assert.Equal(t, aclrecordproto.AclUserPermissions_Admin, aclList.AclState().UserStates()[idA].Permissions)
//assert.Equal(t, aclrecordproto.AclUserPermissions_Writer, aclList.AclState().UserStates()[idB].Permissions)
//assert.Equal(t, aclrecordproto.AclUserPermissions_Reader, aclList.AclState().UserStates()[idC].Permissions)
//assert.Equal(t, aclList.Head().CurrentReadKeyHash, aclList.AclState().CurrentReadKeyId())
//
//var records []*AclRecord
//aclList.Iterate(func(record *AclRecord) (IsContinue bool) {
// records = append(records, record)
// return true
//})
//
//// checking permissions at specific records
//assert.Equal(t, 3, len(records))
//
//_, err = aclList.AclState().StateAtRecord(records[1].Id, idB)
//assert.Error(t, err, "B should have no permissions at record 1")
//
//perm, err := aclList.AclState().StateAtRecord(records[2].Id, idB)
//assert.NoError(t, err, "should have no error with permissions of B in the record 2")
//assert.Equal(t, UserPermissionPair{
// Identity: idB,
// Permission: aclrecordproto.AclUserPermissions_Writer,
//}, perm)
}
func TestAclList_AclState_UserJoinAndRemove(t *testing.T) {
st, err := acllistbuilder.NewListStorageWithTestName("userremoveexample.yml")
require.NoError(t, err, "building storage should not result in error")
keychain := st.(*acllistbuilder.AclListStorageBuilder).GetKeychain()
aclList, err := BuildAclList(st)
require.NoError(t, err, "building acl list should be without error")
idA := keychain.GetIdentity("A")
idB := keychain.GetIdentity("B")
idC := keychain.GetIdentity("C")
// checking final state
assert.Equal(t, aclrecordproto.AclUserPermissions_Admin, aclList.AclState().UserStates()[idA].Permissions)
assert.Equal(t, aclrecordproto.AclUserPermissions_Reader, aclList.AclState().UserStates()[idC].Permissions)
assert.Equal(t, aclList.Head().CurrentReadKeyHash, aclList.AclState().CurrentReadKeyId())
_, exists := aclList.AclState().UserStates()[idB]
assert.Equal(t, false, exists)
var records []*AclRecord
aclList.Iterate(func(record *AclRecord) (IsContinue bool) {
records = append(records, record)
return true
})
// checking permissions at specific records
assert.Equal(t, 4, len(records))
assert.NotEqual(t, records[2].CurrentReadKeyHash, aclList.AclState().CurrentReadKeyId())
perm, err := aclList.AclState().StateAtRecord(records[2].Id, idB)
assert.NoError(t, err, "should have no error with permissions of B in the record 2")
assert.Equal(t, UserPermissionPair{
Identity: idB,
Permission: aclrecordproto.AclUserPermissions_Writer,
}, perm)
_, err = aclList.AclState().StateAtRecord(records[3].Id, idB)
assert.Error(t, err, "B should have no permissions at record 3, because user should be removed")
//st, err := acllistbuilder.NewListStorageWithTestName("userremoveexample.yml")
//require.NoError(t, err, "building storage should not result in error")
//
//keychain := st.(*acllistbuilder.AclListStorageBuilder).GetKeychain()
//
//aclList, err := BuildAclList(st)
//require.NoError(t, err, "building acl list should be without error")
//
//idA := keychain.GetIdentity("A")
//idB := keychain.GetIdentity("B")
//idC := keychain.GetIdentity("C")
//
//// checking final state
//assert.Equal(t, aclrecordproto.AclUserPermissions_Admin, aclList.AclState().UserStates()[idA].Permissions)
//assert.Equal(t, aclrecordproto.AclUserPermissions_Reader, aclList.AclState().UserStates()[idC].Permissions)
//assert.Equal(t, aclList.Head().CurrentReadKeyHash, aclList.AclState().CurrentReadKeyId())
//
//_, exists := aclList.AclState().UserStates()[idB]
//assert.Equal(t, false, exists)
//
//var records []*AclRecord
//aclList.Iterate(func(record *AclRecord) (IsContinue bool) {
// records = append(records, record)
// return true
//})
//
//// checking permissions at specific records
//assert.Equal(t, 4, len(records))
//
//assert.NotEqual(t, records[2].CurrentReadKeyHash, aclList.AclState().CurrentReadKeyId())
//
//perm, err := aclList.AclState().StateAtRecord(records[2].Id, idB)
//assert.NoError(t, err, "should have no error with permissions of B in the record 2")
//assert.Equal(t, UserPermissionPair{
// Identity: idB,
// Permission: aclrecordproto.AclUserPermissions_Writer,
//}, perm)
//
//_, err = aclList.AclState().StateAtRecord(records[3].Id, idB)
//assert.Error(t, err, "B should have no permissions at record 3, because user should be removed")
}

View file

@ -1,194 +0,0 @@
package acllistbuilder
import (
"github.com/anytypeio/any-sync/commonspace/object/acl/aclrecordproto"
"github.com/anytypeio/any-sync/util/crypto"
"github.com/anytypeio/any-sync/util/keys"
"github.com/anytypeio/any-sync/util/keys/asymmetric/encryptionkey"
"github.com/anytypeio/any-sync/util/keys/asymmetric/signingkey"
"hash/fnv"
"strings"
)
type SymKey struct {
Hash uint64
Key *crypto.AESKey
}
type YAMLKeychain struct {
SigningKeysByYAMLName map[string]signingkey.PrivKey
SigningKeysByRealIdentity map[string]signingkey.PrivKey
EncryptionKeysByYAMLName map[string]encryptionkey.PrivKey
ReadKeysByYAMLName map[string]*SymKey
ReadKeysByHash map[uint64]*SymKey
GeneratedIdentities map[string]string
}
func NewKeychain() *YAMLKeychain {
return &YAMLKeychain{
SigningKeysByYAMLName: map[string]signingkey.PrivKey{},
SigningKeysByRealIdentity: map[string]signingkey.PrivKey{},
EncryptionKeysByYAMLName: map[string]encryptionkey.PrivKey{},
GeneratedIdentities: map[string]string{},
ReadKeysByYAMLName: map[string]*SymKey{},
ReadKeysByHash: map[uint64]*SymKey{},
}
}
func (k *YAMLKeychain) ParseKeys(keys *Keys) {
for _, encKey := range keys.Enc {
k.AddEncryptionKey(encKey)
}
for _, signKey := range keys.Sign {
k.AddSigningKey(signKey)
}
for _, readKey := range keys.Read {
k.AddReadKey(readKey)
}
}
func (k *YAMLKeychain) AddEncryptionKey(key *Key) {
if _, exists := k.EncryptionKeysByYAMLName[key.Name]; exists {
return
}
var (
newPrivKey encryptionkey.PrivKey
err error
)
if key.Value == "generated" {
newPrivKey, _, err = encryptionkey.GenerateRandomRSAKeyPair(2048)
if err != nil {
panic(err)
}
} else {
newPrivKey, err = keys.DecodeKeyFromString(key.Value, encryptionkey.NewEncryptionRsaPrivKeyFromBytes, nil)
if err != nil {
panic(err)
}
}
k.EncryptionKeysByYAMLName[key.Name] = newPrivKey
}
func (k *YAMLKeychain) AddSigningKey(key *Key) {
if _, exists := k.SigningKeysByYAMLName[key.Name]; exists {
return
}
var (
newPrivKey signingkey.PrivKey
pubKey signingkey.PubKey
err error
)
if key.Value == "generated" {
newPrivKey, pubKey, err = crypto.GenerateRandomEd25519KeyPair()
if err != nil {
panic(err)
}
} else {
newPrivKey, err = keys.DecodeKeyFromString(key.Value, crypto.NewSigningEd25519PrivKeyFromBytes, nil)
if err != nil {
panic(err)
}
pubKey = newPrivKey.GetPublic()
}
k.SigningKeysByYAMLName[key.Name] = newPrivKey
rawPubKey, err := pubKey.Raw()
if err != nil {
panic(err)
}
encoded := string(rawPubKey)
k.SigningKeysByRealIdentity[encoded] = newPrivKey
k.GeneratedIdentities[key.Name] = encoded
}
func (k *YAMLKeychain) AddReadKey(key *Key) {
if _, exists := k.ReadKeysByYAMLName[key.Name]; exists {
return
}
var (
rkey *crypto.AESKey
err error
)
if key.Value == "generated" {
rkey, err = crypto.NewRandomAES()
if err != nil {
panic("should be able to generate symmetric key")
}
} else if key.Value == "derived" {
signKey, _ := k.SigningKeysByYAMLName[key.Name].Raw()
encKey, _ := k.EncryptionKeysByYAMLName[key.Name].Raw()
rkey, err = aclrecordproto.AclReadKeyDerive(signKey, encKey)
if err != nil {
panic("should be able to derive symmetric key")
}
} else {
rkey, err = crypto.UnmarshallAESKeyString(key.Value)
if err != nil {
panic("should be able to parse symmetric key")
}
}
hasher := fnv.New64()
hasher.Write(rkey.Bytes())
k.ReadKeysByYAMLName[key.Name] = &SymKey{
Hash: hasher.Sum64(),
Key: rkey,
}
k.ReadKeysByHash[hasher.Sum64()] = &SymKey{
Hash: hasher.Sum64(),
Key: rkey,
}
}
func (k *YAMLKeychain) AddKey(key *Key) {
parts := strings.Split(key.Name, ".")
if len(parts) != 3 {
panic("cannot parse a key")
}
switch parts[1] {
case "Signature":
k.AddSigningKey(key)
case "Enc":
k.AddEncryptionKey(key)
case "Read":
k.AddReadKey(key)
default:
panic("incorrect format")
}
}
func (k *YAMLKeychain) GetKey(key string) interface{} {
parts := strings.Split(key, ".")
if len(parts) != 3 {
panic("cannot parse a key")
}
name := parts[2]
switch parts[1] {
case "Sign":
if key, exists := k.SigningKeysByYAMLName[name]; exists {
return key
}
case "Enc":
if key, exists := k.EncryptionKeysByYAMLName[name]; exists {
return key
}
case "Read":
if key, exists := k.ReadKeysByYAMLName[name]; exists {
return key
}
default:
panic("incorrect format")
}
return nil
}
func (k *YAMLKeychain) GetIdentity(name string) string {
return k.GeneratedIdentities[name]
}

View file

@ -1,295 +0,0 @@
package acllistbuilder
import (
"context"
"fmt"
"github.com/anytypeio/any-sync/commonspace/object/acl/aclrecordproto"
"github.com/anytypeio/any-sync/commonspace/object/acl/liststorage"
"github.com/anytypeio/any-sync/commonspace/object/acl/testutils/yamltests"
"github.com/anytypeio/any-sync/util/cidutil"
"github.com/anytypeio/any-sync/util/crypto"
"github.com/anytypeio/any-sync/util/keys/asymmetric/encryptionkey"
"github.com/anytypeio/any-sync/util/keys/asymmetric/signingkey"
"gopkg.in/yaml.v3"
"io/ioutil"
"path"
"time"
"github.com/gogo/protobuf/proto"
)
type AclListStorageBuilder struct {
liststorage.ListStorage
keychain *YAMLKeychain
}
func NewAclListStorageBuilder(keychain *YAMLKeychain) *AclListStorageBuilder {
return &AclListStorageBuilder{
keychain: keychain,
}
}
func NewListStorageWithTestName(name string) (liststorage.ListStorage, error) {
filePath := path.Join(yamltests.Path(), name)
return NewAclListStorageBuilderFromFile(filePath)
}
func NewAclListStorageBuilderFromFile(file string) (*AclListStorageBuilder, error) {
content, err := ioutil.ReadFile(file)
if err != nil {
return nil, err
}
ymlTree := YMLList{}
err = yaml.Unmarshal(content, &ymlTree)
if err != nil {
return nil, err
}
tb := NewAclListStorageBuilder(NewKeychain())
tb.Parse(&ymlTree)
return tb, nil
}
func (t *AclListStorageBuilder) createRaw(rec proto.Marshaler, identity []byte) *aclrecordproto.RawAclRecordWithId {
protoMarshalled, err := rec.Marshal()
if err != nil {
panic("should be able to marshal final acl message!")
}
signature, err := t.keychain.SigningKeysByRealIdentity[string(identity)].Sign(protoMarshalled)
if err != nil {
panic("should be able to sign final acl message!")
}
rawRec := &aclrecordproto.RawAclRecord{
Payload: protoMarshalled,
Signature: signature,
}
rawMarshalled, err := proto.Marshal(rawRec)
if err != nil {
panic(err)
}
id, _ := cidutil.NewCidFromBytes(rawMarshalled)
return &aclrecordproto.RawAclRecordWithId{
Payload: rawMarshalled,
Id: id,
}
}
func (t *AclListStorageBuilder) GetKeychain() *YAMLKeychain {
return t.keychain
}
func (t *AclListStorageBuilder) Parse(l *YMLList) {
// Just to clarify - we are generating new identities for the ones that
// are specified in the yml file, because our identities should be Ed25519
// the same thing is happening for the encryption keys
t.keychain.ParseKeys(&l.Keys)
rawRoot := t.parseRoot(l.Root)
var err error
t.ListStorage, err = liststorage.NewInMemoryAclListStorage(rawRoot.Id, []*aclrecordproto.RawAclRecordWithId{rawRoot})
if err != nil {
panic(err)
}
prevId := rawRoot.Id
for _, rec := range l.Records {
newRecord := t.parseRecord(rec, prevId)
rawRecord := t.createRaw(newRecord, newRecord.Identity)
err = t.AddRawRecord(context.Background(), rawRecord)
if err != nil {
panic(err)
}
prevId = rawRecord.Id
}
t.SetHead(prevId)
}
func (t *AclListStorageBuilder) parseRecord(rec *Record, prevId string) *aclrecordproto.AclRecord {
k := t.keychain.GetKey(rec.ReadKey).(*SymKey)
var aclChangeContents []*aclrecordproto.AclContentValue
for _, ch := range rec.AclChanges {
aclChangeContent := t.parseAclChange(ch)
aclChangeContents = append(aclChangeContents, aclChangeContent)
}
data := &aclrecordproto.AclData{
AclContent: aclChangeContents,
}
bytes, _ := data.Marshal()
return &aclrecordproto.AclRecord{
PrevId: prevId,
Identity: []byte(t.keychain.GetIdentity(rec.Identity)),
Data: bytes,
CurrentReadKeyHash: k.Hash,
Timestamp: time.Now().Unix(),
}
}
func (t *AclListStorageBuilder) parseAclChange(ch *AclChange) (convCh *aclrecordproto.AclContentValue) {
switch {
case ch.UserAdd != nil:
add := ch.UserAdd
encKey := t.keychain.GetKey(add.EncryptionKey).(encryptionkey.PrivKey)
rawKey, _ := encKey.GetPublic().Raw()
convCh = &aclrecordproto.AclContentValue{
Value: &aclrecordproto.AclContentValue_UserAdd{
UserAdd: &aclrecordproto.AclUserAdd{
Identity: []byte(t.keychain.GetIdentity(add.Identity)),
EncryptionKey: rawKey,
EncryptedReadKeys: t.encryptReadKeysWithPubKey(add.EncryptedReadKeys, encKey),
Permissions: t.convertPermission(add.Permission),
},
},
}
case ch.UserJoin != nil:
join := ch.UserJoin
encKey := t.keychain.GetKey(join.EncryptionKey).(encryptionkey.PrivKey)
rawKey, _ := encKey.GetPublic().Raw()
idKey, _ := t.keychain.SigningKeysByYAMLName[join.Identity].GetPublic().Raw()
signKey := t.keychain.GetKey(join.AcceptKey).(signingkey.PrivKey)
signature, err := signKey.Sign(idKey)
if err != nil {
panic(err)
}
acceptPubKey, _ := signKey.GetPublic().Raw()
convCh = &aclrecordproto.AclContentValue{
Value: &aclrecordproto.AclContentValue_UserJoin{
UserJoin: &aclrecordproto.AclUserJoin{
Identity: []byte(t.keychain.GetIdentity(join.Identity)),
EncryptionKey: rawKey,
AcceptSignature: signature,
AcceptPubKey: acceptPubKey,
EncryptedReadKeys: t.encryptReadKeysWithPubKey(join.EncryptedReadKeys, encKey),
},
},
}
case ch.UserInvite != nil:
invite := ch.UserInvite
rawAcceptKey, _ := t.keychain.GetKey(invite.AcceptKey).(signingkey.PrivKey).GetPublic().Raw()
hash := t.keychain.GetKey(invite.EncryptionKey).(*SymKey).Hash
encKey := t.keychain.ReadKeysByHash[hash]
convCh = &aclrecordproto.AclContentValue{
Value: &aclrecordproto.AclContentValue_UserInvite{
UserInvite: &aclrecordproto.AclUserInvite{
AcceptPublicKey: rawAcceptKey,
EncryptSymKeyHash: hash,
EncryptedReadKeys: t.encryptReadKeysWithSymKey(invite.EncryptedReadKeys, encKey.Key),
Permissions: t.convertPermission(invite.Permissions),
},
},
}
case ch.UserPermissionChange != nil:
permissionChange := ch.UserPermissionChange
convCh = &aclrecordproto.AclContentValue{
Value: &aclrecordproto.AclContentValue_UserPermissionChange{
UserPermissionChange: &aclrecordproto.AclUserPermissionChange{
Identity: []byte(t.keychain.GetIdentity(permissionChange.Identity)),
Permissions: t.convertPermission(permissionChange.Permission),
},
},
}
case ch.UserRemove != nil:
remove := ch.UserRemove
newReadKey := t.keychain.GetKey(remove.NewReadKey).(*SymKey)
var replaces []*aclrecordproto.AclReadKeyReplace
for _, id := range remove.IdentitiesLeft {
encKey := t.keychain.EncryptionKeysByYAMLName[id]
rawEncKey, _ := encKey.GetPublic().Raw()
encReadKey, err := encKey.GetPublic().Encrypt(newReadKey.Key.Bytes())
if err != nil {
panic(err)
}
replaces = append(replaces, &aclrecordproto.AclReadKeyReplace{
Identity: []byte(t.keychain.GetIdentity(id)),
EncryptionKey: rawEncKey,
EncryptedReadKey: encReadKey,
})
}
convCh = &aclrecordproto.AclContentValue{
Value: &aclrecordproto.AclContentValue_UserRemove{
UserRemove: &aclrecordproto.AclUserRemove{
Identity: []byte(t.keychain.GetIdentity(remove.RemovedIdentity)),
ReadKeyReplaces: replaces,
},
},
}
}
if convCh == nil {
panic("cannot have empty acl change")
}
return convCh
}
func (t *AclListStorageBuilder) encryptReadKeysWithPubKey(keys []string, encKey encryptionkey.PrivKey) (enc [][]byte) {
for _, k := range keys {
realKey := t.keychain.GetKey(k).(*SymKey).Key.Bytes()
res, err := encKey.GetPublic().Encrypt(realKey)
if err != nil {
panic(err)
}
enc = append(enc, res)
}
return
}
func (t *AclListStorageBuilder) encryptReadKeysWithSymKey(keys []string, key *crypto.AESKey) (enc [][]byte) {
for _, k := range keys {
realKey := t.keychain.GetKey(k).(*SymKey).Key.Bytes()
res, err := key.Encrypt(realKey)
if err != nil {
panic(err)
}
enc = append(enc, res)
}
return
}
func (t *AclListStorageBuilder) convertPermission(perm string) aclrecordproto.AclUserPermissions {
switch perm {
case "admin":
return aclrecordproto.AclUserPermissions_Admin
case "writer":
return aclrecordproto.AclUserPermissions_Writer
case "reader":
return aclrecordproto.AclUserPermissions_Reader
default:
panic(fmt.Sprintf("incorrect permission: %s", perm))
}
}
func (t *AclListStorageBuilder) traverseFromHead(f func(rec *aclrecordproto.AclRecord, id string) error) (err error) {
panic("this was removed, add if needed")
}
func (t *AclListStorageBuilder) parseRoot(root *Root) (rawRoot *aclrecordproto.RawAclRecordWithId) {
rawSignKey, _ := t.keychain.SigningKeysByYAMLName[root.Identity].GetPublic().Raw()
rawEncKey, _ := t.keychain.EncryptionKeysByYAMLName[root.Identity].GetPublic().Raw()
readKey := t.keychain.ReadKeysByYAMLName[root.Identity]
aclRoot := &aclrecordproto.AclRoot{
Identity: rawSignKey,
EncryptionKey: rawEncKey,
SpaceId: root.SpaceId,
EncryptedReadKey: nil,
DerivationScheme: "scheme",
CurrentReadKeyHash: readKey.Hash,
}
return t.createRaw(aclRoot, rawSignKey)
}

View file

@ -1,11 +0,0 @@
//go:build ((!linux && !darwin) || android || ios || nographviz || !cgo) && !amd64
// +build !linux,!darwin android ios nographviz !cgo
// +build !amd64
package acllistbuilder
import "fmt"
func (t *AclListStorageBuilder) Graph() (string, error) {
return "", fmt.Errorf("building graphs is not supported")
}

View file

@ -1,122 +0,0 @@
//go:build (linux || darwin) && !android && !ios && !nographviz && cgo && (amd64 || arm64)
// +build linux darwin
// +build !android
// +build !ios
// +build !nographviz
// +build cgo
// +build amd64 arm64
package acllistbuilder
import (
"fmt"
"strings"
"unicode"
"github.com/anytypeio/any-sync/commonspace/object/acl/aclrecordproto"
"github.com/gogo/protobuf/proto"
"github.com/awalterschulze/gographviz"
)
// To quickly look at visualized string you can use https://dreampuf.github.io/GraphvizOnline
type EdgeParameters struct {
style string
color string
label string
}
func (t *AclListStorageBuilder) Graph() (string, error) {
// TODO: check updates on https://github.com/goccy/go-graphviz/issues/52 or make a fix yourself to use better library here
graph := gographviz.NewGraph()
graph.SetName("G")
graph.SetDir(true)
var nodes = make(map[string]struct{})
var addNodes = func(r *aclrecordproto.AclRecord, id string) error {
style := "solid"
var chSymbs []string
aclData := &aclrecordproto.AclData{}
err := proto.Unmarshal(r.GetData(), aclData)
if err != nil {
return err
}
for _, chc := range aclData.AclContent {
tp := fmt.Sprintf("%T", chc.Value)
tp = strings.Replace(tp, "AclChangeAclContentValueValueOf", "", 1)
res := ""
for _, ts := range tp {
if unicode.IsUpper(ts) {
res += string(ts)
}
}
chSymbs = append(chSymbs, res)
}
shortId := id
label := fmt.Sprintf("Id: %s\nChanges: %s\n",
shortId,
strings.Join(chSymbs, ","),
)
e := graph.AddNode("G", "\""+id+"\"", map[string]string{
"label": "\"" + label + "\"",
"style": "\"" + style + "\"",
})
if e != nil {
return e
}
nodes[id] = struct{}{}
return nil
}
var createEdge = func(firstId, secondId string, params EdgeParameters) error {
_, exists := nodes[firstId]
if !exists {
return fmt.Errorf("no such node")
}
_, exists = nodes[secondId]
if !exists {
return fmt.Errorf("no previous node")
}
err := graph.AddEdge("\""+firstId+"\"", "\""+secondId+"\"", true, map[string]string{
"color": params.color,
"style": params.style,
})
if err != nil {
return err
}
return nil
}
var addLinks = func(r *aclrecordproto.AclRecord, id string) error {
if r.PrevId == "" {
return nil
}
err := createEdge(id, r.PrevId, EdgeParameters{
style: "dashed",
color: "red",
})
if err != nil {
return err
}
return nil
}
err := t.traverseFromHead(addNodes)
if err != nil {
return "", err
}
err = t.traverseFromHead(addLinks)
if err != nil {
return "", err
}
return graph.String(), nil
}

View file

@ -1,70 +0,0 @@
package acllistbuilder
type Key struct {
Name string `yaml:"name"`
Value string `yaml:"value"`
}
type Keys struct {
Derived string `yaml:"Derived"`
Enc []*Key `yaml:"Enc"`
Sign []*Key `yaml:"Sign"`
Read []*Key `yaml:"Read"`
}
type AclChange struct {
UserAdd *struct {
Identity string `yaml:"identity"`
EncryptionKey string `yaml:"encryptionKey"`
EncryptedReadKeys []string `yaml:"encryptedReadKeys"`
Permission string `yaml:"permission"`
} `yaml:"userAdd"`
UserJoin *struct {
Identity string `yaml:"identity"`
EncryptionKey string `yaml:"encryptionKey"`
AcceptKey string `yaml:"acceptKey"`
EncryptedReadKeys []string `yaml:"encryptedReadKeys"`
} `yaml:"userJoin"`
UserInvite *struct {
AcceptKey string `yaml:"acceptKey"`
EncryptionKey string `yaml:"encryptionKey"`
EncryptedReadKeys []string `yaml:"encryptedReadKeys"`
Permissions string `yaml:"permissions"`
} `yaml:"userInvite"`
UserRemove *struct {
RemovedIdentity string `yaml:"removedIdentity"`
NewReadKey string `yaml:"newReadKey"`
IdentitiesLeft []string `yaml:"identitiesLeft"`
} `yaml:"userRemove"`
UserPermissionChange *struct {
Identity string `yaml:"identity"`
Permission string `yaml:"permission"`
}
}
type Record struct {
Identity string `yaml:"identity"`
AclChanges []*AclChange `yaml:"aclChanges"`
ReadKey string `yaml:"readKey"`
}
type Header struct {
FirstChangeId string `yaml:"firstChangeId"`
IsWorkspace bool `yaml:"isWorkspace"`
}
type Root struct {
Identity string `yaml:"identity"`
SpaceId string `yaml:"spaceId"`
}
type YMLList struct {
Root *Root
Records []*Record `yaml:"records"`
Keys Keys `yaml:"keys"`
}

View file

@ -1,15 +0,0 @@
package yamltests
import (
"path/filepath"
"runtime"
)
var (
_, b, _, _ = runtime.Caller(0)
basepath = filepath.Dir(b)
)
func Path() string {
return basepath
}

View file

@ -1,53 +0,0 @@
root:
identity: A
spaceId: space
records:
- identity: A
aclChanges:
- userInvite:
acceptKey: key.Sign.Onetime1
encryptionKey: key.Read.EncKey
encryptedReadKeys: [key.Read.A]
permissions: writer
- userAdd:
identity: C
permission: reader
encryptionKey: key.Enc.C
encryptedReadKeys: [key.Read.A]
readKey: key.Read.A
- identity: B
aclChanges:
- userJoin:
identity: B
encryptionKey: key.Enc.B
acceptKey: key.Sign.Onetime1
encryptedReadKeys: [key.Read.A]
readKey: key.Read.A
keys:
Enc:
- name: A
value: generated
- name: B
value: generated
- name: C
value: generated
- name: D
value: generated
- name: Onetime1
value: generated
Sign:
- name: A
value: generated
- name: B
value: generated
- name: C
value: generated
- name: D
value: generated
- name: Onetime1
value: generated
Read:
- name: A
value: derived
- name: EncKey
value: generated

View file

@ -1,58 +0,0 @@
root:
identity: A
spaceId: space
records:
- identity: A
aclChanges:
- userInvite:
acceptKey: key.Sign.Onetime1
encryptionKey: key.Read.EncKey
encryptedReadKeys: [key.Read.A]
permissions: writer
- userAdd:
identity: C
permission: reader
encryptionKey: key.Enc.C
encryptedReadKeys: [key.Read.A]
readKey: key.Read.A
- identity: B
aclChanges:
- userJoin:
identity: B
encryptionKey: key.Enc.B
acceptKey: key.Sign.Onetime1
encryptedReadKeys: [key.Read.A]
readKey: key.Read.A
- identity: A
aclChanges:
- userRemove:
removedIdentity: B
newReadKey: key.Read.2
identitiesLeft: [A, C]
readKey: key.Read.2
keys:
Enc:
- name: A
value: generated
- name: B
value: generated
- name: C
value: generated
- name: Onetime1
value: generated
Sign:
- name: A
value: generated
- name: B
value: generated
- name: C
value: generated
- name: Onetime1
value: generated
Read:
- name: A
value: derived
- name: 2
value: generated
- name: EncKey
value: generated

View file

@ -3,6 +3,7 @@ package objecttree
import (
"errors"
"github.com/anytypeio/any-sync/commonspace/object/tree/treechangeproto"
"github.com/anytypeio/any-sync/util/crypto"
"github.com/gogo/protobuf/proto"
)
@ -21,7 +22,7 @@ type Change struct {
IsSnapshot bool
Timestamp int64
ReadKeyId string
Identity string
Identity crypto.PubKey
Data []byte
Model interface{}
@ -32,7 +33,7 @@ type Change struct {
Signature []byte
}
func NewChange(id string, ch *treechangeproto.TreeChange, signature []byte) *Change {
func NewChange(id string, identity crypto.PubKey, ch *treechangeproto.TreeChange, signature []byte) *Change {
return &Change{
Next: nil,
PreviousIds: ch.TreeHeadIds,
@ -43,12 +44,12 @@ func NewChange(id string, ch *treechangeproto.TreeChange, signature []byte) *Cha
Data: ch.ChangesData,
SnapshotId: ch.SnapshotBaseId,
IsSnapshot: ch.IsSnapshot,
Identity: string(ch.Identity),
Identity: identity,
Signature: signature,
}
}
func NewChangeFromRoot(id string, ch *treechangeproto.RootChange, signature []byte) *Change {
func NewChangeFromRoot(id string, identity crypto.PubKey, ch *treechangeproto.RootChange, signature []byte) *Change {
changeInfo := &treechangeproto.TreeChangeInfo{
ChangeType: ch.ChangeType,
ChangePayload: ch.ChangePayload,
@ -60,7 +61,7 @@ func NewChangeFromRoot(id string, ch *treechangeproto.RootChange, signature []by
Id: id,
IsSnapshot: true,
Timestamp: ch.Timestamp,
Identity: string(ch.Identity),
Identity: identity,
Signature: signature,
Data: data,
Model: changeInfo,

View file

@ -2,11 +2,9 @@ package objecttree
import (
"errors"
"github.com/anytypeio/any-sync/commonspace/object/keychain"
"github.com/anytypeio/any-sync/commonspace/object/tree/treechangeproto"
"github.com/anytypeio/any-sync/util/cidutil"
"github.com/anytypeio/any-sync/util/crypto"
"github.com/anytypeio/any-sync/util/keys/asymmetric/signingkey"
"github.com/gogo/protobuf/proto"
"time"
)
@ -18,17 +16,15 @@ type BuilderContent struct {
AclHeadId string
SnapshotBaseId string
ReadKeyId string
Identity []byte
IsSnapshot bool
SigningKey signingkey.PrivKey
ReadKey *crypto.AESKey
PrivKey crypto.PrivKey
ReadKey crypto.SymKey
Content []byte
}
type InitialContent struct {
AclHeadId string
Identity []byte
SigningKey signingkey.PrivKey
PrivKey crypto.PrivKey
SpaceId string
Seed []byte
ChangeType string
@ -65,10 +61,10 @@ type ChangeBuilder interface {
type changeBuilder struct {
rootChange *treechangeproto.RawTreeChangeWithId
keys *keychain.Keychain
keys crypto.KeyStorage
}
func NewChangeBuilder(keys *keychain.Keychain, rootChange *treechangeproto.RawTreeChangeWithId) ChangeBuilder {
func NewChangeBuilder(keys crypto.KeyStorage, rootChange *treechangeproto.RawTreeChangeWithId) ChangeBuilder {
return &changeBuilder{keys: keys, rootChange: rootChange}
}
@ -97,15 +93,9 @@ func (c *changeBuilder) Unmarshall(rawIdChange *treechangeproto.RawTreeChangeWit
}
if verify {
var identityKey signingkey.PubKey
identityKey, err = c.keys.GetOrAdd(ch.Identity)
if err != nil {
return
}
// verifying signature
var res bool
res, err = identityKey.Verify(raw.Payload, raw.Signature)
res, err = ch.Identity.Verify(raw.Payload, raw.Signature)
if err != nil {
return
}
@ -122,10 +112,14 @@ func (c *changeBuilder) SetRootRawChange(rawIdChange *treechangeproto.RawTreeCha
}
func (c *changeBuilder) BuildRoot(payload InitialContent) (ch *Change, rawIdChange *treechangeproto.RawTreeChangeWithId, err error) {
identity, err := payload.PrivKey.GetPublic().Marshall()
if err != nil {
return
}
change := &treechangeproto.RootChange{
AclHeadId: payload.AclHeadId,
Timestamp: payload.Timestamp,
Identity: payload.Identity,
Identity: identity,
ChangeType: payload.ChangeType,
ChangePayload: payload.ChangePayload,
SpaceId: payload.SpaceId,
@ -135,7 +129,7 @@ func (c *changeBuilder) BuildRoot(payload InitialContent) (ch *Change, rawIdChan
if err != nil {
return
}
signature, err := payload.SigningKey.Sign(marshalledChange)
signature, err := payload.PrivKey.Sign(marshalledChange)
if err != nil {
return
}
@ -151,7 +145,7 @@ func (c *changeBuilder) BuildRoot(payload InitialContent) (ch *Change, rawIdChan
if err != nil {
return
}
ch = NewChangeFromRoot(id, change, signature)
ch = NewChangeFromRoot(id, payload.PrivKey.GetPublic(), change, signature)
rawIdChange = &treechangeproto.RawTreeChangeWithId{
RawChange: marshalledRawChange,
Id: id,
@ -160,13 +154,17 @@ func (c *changeBuilder) BuildRoot(payload InitialContent) (ch *Change, rawIdChan
}
func (c *changeBuilder) Build(payload BuilderContent) (ch *Change, rawIdChange *treechangeproto.RawTreeChangeWithId, err error) {
identity, err := payload.PrivKey.GetPublic().Marshall()
if err != nil {
return
}
change := &treechangeproto.TreeChange{
TreeHeadIds: payload.TreeHeadIds,
AclHeadId: payload.AclHeadId,
SnapshotBaseId: payload.SnapshotBaseId,
ReadKeyId: payload.ReadKeyId,
Timestamp: time.Now().Unix(),
Identity: payload.Identity,
Identity: identity,
IsSnapshot: payload.IsSnapshot,
}
if payload.ReadKey != nil {
@ -183,7 +181,7 @@ func (c *changeBuilder) Build(payload BuilderContent) (ch *Change, rawIdChange *
if err != nil {
return
}
signature, err := payload.SigningKey.Sign(marshalledChange)
signature, err := payload.PrivKey.Sign(marshalledChange)
if err != nil {
return
}
@ -199,7 +197,7 @@ func (c *changeBuilder) Build(payload BuilderContent) (ch *Change, rawIdChange *
if err != nil {
return
}
ch = NewChange(id, change, signature)
ch = NewChange(id, payload.PrivKey.GetPublic(), change, signature)
rawIdChange = &treechangeproto.RawTreeChangeWithId{
RawChange: marshalledRawChange,
Id: id,
@ -211,6 +209,10 @@ func (c *changeBuilder) Marshall(ch *Change) (raw *treechangeproto.RawTreeChange
if c.isRoot(ch.Id) {
return c.rootChange, nil
}
identity, err := ch.Identity.Marshall()
if err != nil {
return
}
treeChange := &treechangeproto.TreeChange{
TreeHeadIds: ch.PreviousIds,
AclHeadId: ch.AclHeadId,
@ -218,7 +220,7 @@ func (c *changeBuilder) Marshall(ch *Change) (raw *treechangeproto.RawTreeChange
ChangesData: ch.Data,
ReadKeyId: ch.ReadKeyId,
Timestamp: ch.Timestamp,
Identity: []byte(ch.Identity),
Identity: identity,
IsSnapshot: ch.IsSnapshot,
}
var marshalled []byte
@ -243,13 +245,18 @@ func (c *changeBuilder) Marshall(ch *Change) (raw *treechangeproto.RawTreeChange
}
func (c *changeBuilder) unmarshallRawChange(raw *treechangeproto.RawTreeChange, id string) (ch *Change, err error) {
var key crypto.PubKey
if c.isRoot(id) {
unmarshalled := &treechangeproto.RootChange{}
err = proto.Unmarshal(raw.Payload, unmarshalled)
if err != nil {
return
}
ch = NewChangeFromRoot(id, unmarshalled, raw.Signature)
key, err = c.keys.PubKeyFromProto(unmarshalled.Identity)
if err != nil {
return
}
ch = NewChangeFromRoot(id, key, unmarshalled, raw.Signature)
return
}
unmarshalled := &treechangeproto.TreeChange{}
@ -257,8 +264,11 @@ func (c *changeBuilder) unmarshallRawChange(raw *treechangeproto.RawTreeChange,
if err != nil {
return
}
ch = NewChange(id, unmarshalled, raw.Signature)
key, err = c.keys.PubKeyFromProto(unmarshalled.Identity)
if err != nil {
return
}
ch = NewChange(id, key, unmarshalled, raw.Signature)
return
}

View file

@ -99,7 +99,7 @@ type objectTree struct {
root *Change
tree *Tree
keys map[uint64]*crypto.AESKey
keys map[string]crypto.SymKey
// buffers
difSnapshotBuf []*treechangeproto.RawTreeChangeWithId
@ -224,19 +224,20 @@ func (ot *objectTree) prepareBuilderContent(content SignableChangeContent) (cnt
defer ot.aclList.RUnlock()
var (
state = ot.aclList.AclState() // special method for own keys
readKey *crypto.AESKey
readKeyHash uint64
state = ot.aclList.AclState() // special method for own keys
readKey crypto.SymKey
pubKey = content.Key.GetPublic()
readKeyId string
)
canWrite := state.HasPermission(content.Identity, aclrecordproto.AclUserPermissions_Writer) ||
state.HasPermission(content.Identity, aclrecordproto.AclUserPermissions_Admin)
canWrite := state.HasPermission(pubKey, aclrecordproto.AclUserPermissions_Writer) ||
state.HasPermission(pubKey, aclrecordproto.AclUserPermissions_Admin)
if !canWrite {
err = list.ErrInsufficientPermissions
return
}
if content.IsEncrypted {
readKeyHash = state.CurrentReadKeyId()
readKeyId = state.CurrentReadKeyId()
readKey, err = state.CurrentReadKey()
if err != nil {
return
@ -246,10 +247,9 @@ func (ot *objectTree) prepareBuilderContent(content SignableChangeContent) (cnt
TreeHeadIds: ot.tree.Heads(),
AclHeadId: ot.aclList.Head().Id,
SnapshotBaseId: ot.tree.RootId(),
ReadKeyId: readKeyHash,
Identity: content.Identity,
ReadKeyId: readKeyId,
IsSnapshot: content.IsSnapshot,
SigningKey: content.Key,
PrivKey: content.Key,
ReadKey: readKey,
Content: content.Data,
}
@ -488,7 +488,7 @@ func (ot *objectTree) IterateFrom(id string, convert ChangeConvertFunc, iterate
}
decrypt := func(c *Change) (decrypted []byte, err error) {
// the change is not encrypted
if c.ReadKeyId == 0 {
if c.ReadKeyId == "" {
decrypted = c.Data
return
}

View file

@ -2,21 +2,18 @@ package objecttree
import (
"github.com/anytypeio/any-sync/commonspace/object/acl/list"
"github.com/anytypeio/any-sync/commonspace/object/keychain"
"github.com/anytypeio/any-sync/commonspace/object/tree/treechangeproto"
"github.com/anytypeio/any-sync/commonspace/object/tree/treestorage"
"github.com/anytypeio/any-sync/util/crypto"
"github.com/anytypeio/any-sync/util/keys/asymmetric/signingkey"
"math/rand"
"time"
)
type ObjectTreeCreatePayload struct {
SignKey signingkey.PrivKey
PrivKey crypto.PrivKey
ChangeType string
ChangePayload []byte
SpaceId string
Identity []byte
IsEncrypted bool
}
@ -40,9 +37,7 @@ func defaultObjectTreeDeps(
rootChange *treechangeproto.RawTreeChangeWithId,
treeStorage treestorage.TreeStorage,
aclList list.AclList) objectTreeDeps {
keychain := keychain.NewKeychain()
changeBuilder := NewChangeBuilder(keychain, rootChange)
changeBuilder := NewChangeBuilder(aclList.KeyStorage(), rootChange)
treeBuilder := newTreeBuilder(treeStorage, changeBuilder)
return objectTreeDeps{
changeBuilder: changeBuilder,
@ -167,8 +162,7 @@ func createObjectTreeRoot(
}
cnt := InitialContent{
AclHeadId: aclHeadId,
Identity: payload.Identity,
SigningKey: payload.SignKey,
PrivKey: payload.PrivKey,
SpaceId: payload.SpaceId,
ChangeType: payload.ChangeType,
ChangePayload: payload.ChangePayload,
@ -176,7 +170,7 @@ func createObjectTreeRoot(
Seed: seed,
}
_, root, err = NewChangeBuilder(keychain.NewKeychain(), nil).BuildRoot(cnt)
_, root, err = NewChangeBuilder(aclList.KeyStorage(), nil).BuildRoot(cnt)
return
}
@ -189,7 +183,7 @@ func buildObjectTree(deps objectTreeDeps) (ObjectTree, error) {
aclList: deps.aclList,
changeBuilder: deps.changeBuilder,
rawChangeLoader: deps.rawChangeLoader,
keys: make(map[uint64]*crypto.AESKey),
keys: make(map[string]crypto.SymKey),
newChangesBuf: make([]*Change, 0, 10),
difSnapshotBuf: make([]*treechangeproto.RawTreeChangeWithId, 0, 10),
notSeenIdxBuf: make([]int, 0, 10),
@ -225,7 +219,7 @@ func buildHistoryTree(deps objectTreeDeps, params HistoryTreeParams) (ht History
aclList: deps.aclList,
changeBuilder: deps.changeBuilder,
rawChangeLoader: deps.rawChangeLoader,
keys: make(map[uint64]*crypto.AESKey),
keys: make(map[string]crypto.SymKey),
newChangesBuf: make([]*Change, 0, 10),
difSnapshotBuf: make([]*treechangeproto.RawTreeChangeWithId, 0, 10),
notSeenIdxBuf: make([]int, 0, 10),

View file

@ -50,7 +50,7 @@ func (v *objectTreeValidator) ValidateNewChanges(tree *Tree, aclList list.AclLis
func (v *objectTreeValidator) validateChange(tree *Tree, aclList list.AclList, c *Change) (err error) {
var (
perm list.UserPermissionPair
perm list.AclUserState
state = aclList.AclState()
)
// checking if the user could write
@ -59,7 +59,7 @@ func (v *objectTreeValidator) validateChange(tree *Tree, aclList list.AclList, c
return
}
if perm.Permission != aclrecordproto.AclUserPermissions_Writer && perm.Permission != aclrecordproto.AclUserPermissions_Admin {
if perm.Permissions != aclrecordproto.AclUserPermissions_Writer && perm.Permissions != aclrecordproto.AclUserPermissions_Admin {
err = list.ErrInsufficientPermissions
return
}

View file

@ -1,13 +1,12 @@
package objecttree
import (
"github.com/anytypeio/any-sync/util/keys/asymmetric/signingkey"
"github.com/anytypeio/any-sync/util/crypto"
)
type SignableChangeContent struct {
Data []byte
Key signingkey.PrivKey
Identity []byte
Key crypto.PrivKey
IsSnapshot bool
IsEncrypted bool
}

View file

@ -99,7 +99,7 @@ func storagePayloadForSpaceCreate(payload SpaceCreatePayload) (storagePayload sp
_, settingsRoot, err := builder.BuildRoot(objecttree.InitialContent{
AclHeadId: rawWithId.Id,
Identity: aclRoot.Identity,
SigningKey: payload.SigningKey,
PrivKey: payload.SigningKey,
SpaceId: spaceId,
Seed: spaceSettingsSeed,
ChangeType: SpaceReserved,
@ -201,7 +201,7 @@ func storagePayloadForSpaceDerive(payload SpaceDerivePayload) (storagePayload sp
_, settingsRoot, err := builder.BuildRoot(objecttree.InitialContent{
AclHeadId: rawWithId.Id,
Identity: aclRoot.Identity,
SigningKey: payload.SigningKey,
PrivKey: payload.SigningKey,
SpaceId: spaceId,
ChangeType: SpaceReserved,
})

View file

@ -146,7 +146,7 @@ func TestSettingsObject_DeleteObject(t *testing.T) {
fx.doc.state = &settingsstate.State{LastIteratedId: "someId"}
fx.changeFactory.EXPECT().CreateObjectDeleteChange(delId, fx.doc.state, false).Return(res, nil)
accountData := &accountdata.AccountData{
accountData := &accountdata.AccountKeys{
Identity: []byte("id"),
PeerKey: nil,
SignKey: &crypto.Ed25519PrivKey{},

View file

@ -25,12 +25,12 @@ func (n noVerifyChecker) CheckCredential(sc sec.SecureConn, cred *handshakeproto
return nil, nil
}
func newPeerSignVerifier(account *accountdata.AccountData) handshake.CredentialChecker {
func newPeerSignVerifier(account *accountdata.AccountKeys) handshake.CredentialChecker {
return &peerSignVerifier{account: account}
}
type peerSignVerifier struct {
account *accountdata.AccountData
account *accountdata.AccountKeys
}
func (p *peerSignVerifier) MakeCredentials(sc sec.SecureConn) *handshakeproto.Credentials {

View file

@ -38,7 +38,7 @@ func TestPeerSignVerifier_CheckCredential(t *testing.T) {
assert.EqualError(t, err, handshake.ErrInvalidCredentials.Error())
}
func newTestAccData(t *testing.T) *accountdata.AccountData {
func newTestAccData(t *testing.T) *accountdata.AccountKeys {
as := accounttest.AccountTestService{}
require.NoError(t, as.Init(nil))
return as.Account()

View file

@ -45,7 +45,7 @@ type SecureService interface {
type secureService struct {
p2pTr *libp2ptls.Transport
account *accountdata.AccountData
account *accountdata.AccountKeys
key crypto.PrivKey
nodeconf nodeconf.Service

View file

@ -13,7 +13,7 @@ import (
// AccountTestService provides service for test purposes, generates new random account every Init
type AccountTestService struct {
acc *accountdata.AccountData
acc *accountdata.AccountKeys
}
func (s *AccountTestService) Init(a *app.App) (err error) {
@ -43,7 +43,7 @@ func (s *AccountTestService) Init(a *app.App) (err error) {
if err != nil {
return err
}
s.acc = &accountdata.AccountData{
s.acc = &accountdata.AccountKeys{
Identity: ident,
PeerKey: peerKey,
SignKey: signKey,
@ -57,7 +57,7 @@ func (s *AccountTestService) Name() (name string) {
return accountService.CName
}
func (s *AccountTestService) Account() *accountdata.AccountData {
func (s *AccountTestService) Account() *accountdata.AccountKeys {
return s.acc
}

View file

@ -24,9 +24,14 @@ type Ed25519PrivKey struct {
// Ed25519PubKey is an ed25519 public key.
type Ed25519PubKey struct {
pubKey ed25519.PublicKey
pubCurve *[32]byte
once sync.Once
pubKey ed25519.PublicKey
pubCurve *[32]byte
curveOnce sync.Once
marshallOnce sync.Once
marshalled []byte
marshallErr error
}
func NewEd25519PrivKey(privKey ed25519.PrivateKey) PrivKey {
@ -132,7 +137,7 @@ func (k *Ed25519PubKey) Raw() ([]byte, error) {
// Encrypt message
func (k *Ed25519PubKey) Encrypt(msg []byte) (data []byte, err error) {
k.once.Do(func() {
k.curveOnce.Do(func() {
pubCurve := Ed25519PublicKeyToCurve25519(k.pubKey)
k.pubCurve = (*[32]byte)(pubCurve)
})
@ -161,11 +166,14 @@ func (k *Ed25519PubKey) Verify(data []byte, sig []byte) (bool, error) {
}
func (k *Ed25519PubKey) Marshall() ([]byte, error) {
msg := &cryptoproto.Key{
Type: cryptoproto.KeyType_Ed25519Public,
Data: k.pubKey,
}
return proto.Marshal(msg)
k.marshallOnce.Do(func() {
msg := &cryptoproto.Key{
Type: cryptoproto.KeyType_Ed25519Public,
Data: k.pubKey,
}
k.marshalled, k.marshallErr = proto.Marshal(msg)
})
return k.marshalled, k.marshallErr
}
// UnmarshalEd25519PublicKey returns a public key from input bytes.