package list import ( "errors" "github.com/anyproto/protobuf/proto" "go.uber.org/zap" "golang.org/x/exp/slices" "github.com/anyproto/any-sync/app/logger" "github.com/anyproto/any-sync/commonspace/object/acl/aclrecordproto" "github.com/anyproto/any-sync/commonspace/object/acl/recordverifier" "github.com/anyproto/any-sync/util/crypto" ) var log = logger.NewNamedSugared("common.commonspace.acllist") var ( ErrNoSuchAccount = errors.New("no such account") ErrPendingRequest = errors.New("already exists pending request") ErrUnexpectedContentType = errors.New("unexpected content type") ErrIncorrectIdentity = errors.New("incorrect identity") ErrIncorrectInviteKey = errors.New("incorrect invite key") ErrFailedToDecrypt = errors.New("failed to decrypt key") ErrNoMetadataKey = errors.New("no metadata key") ErrNoSuchRecord = errors.New("no such record") ErrNoSuchRequest = errors.New("no such request") ErrNoSuchInvite = errors.New("no such invite") ErrInsufficientPermissions = errors.New("insufficient permissions") ErrIsOwner = errors.New("can't be made by owner") ErrIncorrectNumberOfAccounts = errors.New("incorrect number of accounts") ErrDuplicateAccounts = errors.New("duplicate accounts") ErrNoReadKey = errors.New("no read key") ErrIncorrectReadKey = errors.New("incorrect read key") ErrInvalidSignature = errors.New("signature is invalid") ErrIncorrectRoot = errors.New("incorrect root") ErrIncorrectRecordSequence = errors.New("incorrect prev id of a record") ErrMetadataTooLarge = errors.New("metadata size too large") ErrOwnerNotFound = errors.New("owner not found") ) const MaxMetadataLen = 1024 type UserPermissionPair struct { Identity crypto.PubKey Permission aclrecordproto.AclUserPermissions } type AclKeys struct { ReadKey crypto.SymKey MetadataPrivKey crypto.PrivKey MetadataPubKey crypto.PubKey oldEncryptedReadKey []byte encMetadatKey []byte } type Invite struct { Key crypto.PubKey Type aclrecordproto.AclInviteType Permissions AclPermissions encryptedKey []byte } type AclState struct { id string // keys represent current keys of the acl keys map[string]AclKeys // accountStates is a map pubKey -> state which defines current account state accountStates map[string]AccountState // inviteKeys is a map recordId -> invite invites map[string]Invite // requestRecords is a map recordId -> RequestRecord requestRecords map[string]RequestRecord // pendingRequests is a map pubKey -> recordId pendingRequests map[string]string // readKeyChanges is a list of records containing read key changes readKeyChanges []string key crypto.PrivKey pubKey crypto.PubKey keyStore crypto.KeyStorage lastRecordId string contentValidator ContentValidator list *aclList } func newAclStateWithKeys( rootRecord *AclRecord, key crypto.PrivKey, verifier recordverifier.AcceptorVerifier) (st *AclState, err error) { st = &AclState{ id: rootRecord.Id, key: key, pubKey: key.GetPublic(), keys: make(map[string]AclKeys), accountStates: make(map[string]AccountState), invites: make(map[string]Invite), requestRecords: make(map[string]RequestRecord), pendingRequests: make(map[string]string), keyStore: crypto.NewKeyStorage(), } st.contentValidator = newContentValidator(st.keyStore, st, verifier) err = st.applyRoot(rootRecord) if err != nil { return } return st, nil } func newAclState(rootRecord *AclRecord, verifier recordverifier.AcceptorVerifier) (st *AclState, err error) { st = &AclState{ id: rootRecord.Id, keys: make(map[string]AclKeys), accountStates: make(map[string]AccountState), invites: make(map[string]Invite), requestRecords: make(map[string]RequestRecord), pendingRequests: make(map[string]string), keyStore: crypto.NewKeyStorage(), } st.contentValidator = newContentValidator(st.keyStore, st, verifier) err = st.applyRoot(rootRecord) if err != nil { return } return st, nil } func (st *AclState) Identity() crypto.PubKey { return st.pubKey } func (st *AclState) Validator() ContentValidator { return st.contentValidator } func (st *AclState) CurrentReadKeyId() string { return st.readKeyChanges[len(st.readKeyChanges)-1] } func (st *AclState) ReadKeyForAclId(id string) (string, error) { recIdx, ok := st.list.indexes[id] if !ok { return "", ErrNoSuchRecord } for i := len(st.readKeyChanges) - 1; i >= 0; i-- { recId := st.readKeyChanges[i] if recIdx >= st.list.indexes[recId] { return recId, nil } } return "", ErrNoSuchRecord } func (st *AclState) AccountKey() crypto.PrivKey { return st.key } func (st *AclState) CurrentReadKey() (crypto.SymKey, error) { curKeys, exists := st.keys[st.CurrentReadKeyId()] if !exists { return nil, ErrNoReadKey } return curKeys.ReadKey, nil } func (st *AclState) CurrentMetadataKey() (crypto.PubKey, error) { curKeys, exists := st.keys[st.CurrentReadKeyId()] if !exists { return nil, ErrNoMetadataKey } return curKeys.MetadataPubKey, nil } func (st *AclState) FirstMetadataKey() (crypto.PrivKey, error) { if firstKey, ok := st.keys[st.id]; ok && firstKey.MetadataPrivKey != nil { return firstKey.MetadataPrivKey, nil } return nil, ErrNoMetadataKey } func (st *AclState) Keys() map[string]AclKeys { return st.keys } func (st *AclState) PermissionsAtRecord(id string, pubKey crypto.PubKey) (AclPermissions, error) { if !st.list.HasHead(id) { return AclPermissionsNone, ErrNoSuchRecord } accountState, ok := st.accountStates[mapKeyFromPubKey(pubKey)] if !ok { return AclPermissionsNone, ErrNoSuchAccount } perms := closestPermissions(accountState, id, st.list.isAfterNoCheck) return perms, nil } func (st *AclState) CurrentAccounts() []AccountState { var res []AccountState for _, state := range st.accountStates { res = append(res, state) } return res } func (st *AclState) HadReadPermissions(identity crypto.PubKey) (had bool) { state, exists := st.accountStates[mapKeyFromPubKey(identity)] if !exists { return false } for _, perm := range state.PermissionChanges { if !perm.Permission.NoPermissions() { return true } } return false } func (st *AclState) Invites(inviteType ...aclrecordproto.AclInviteType) []Invite { var invites []Invite for _, inv := range st.invites { if len(inviteType) > 0 && !slices.Contains(inviteType, inv.Type) { continue } invites = append(invites, inv) } return invites } func (st *AclState) Key() crypto.PrivKey { return st.key } func (st *AclState) InviteIds() []string { var invites []string for invId := range st.invites { invites = append(invites, invId) } return invites } func (st *AclState) RequestIds() []string { var requests []string for reqId := range st.requestRecords { requests = append(requests, reqId) } return requests } func (st *AclState) DecryptInvite(invitePk crypto.PrivKey) (key crypto.SymKey, err error) { if invitePk == nil { return nil, ErrNoReadKey } for _, invite := range st.invites { if invite.Key.Equals(invitePk.GetPublic()) { res, err := st.unmarshallDecryptReadKey(invite.encryptedKey, invitePk.Decrypt) if err != nil { return nil, err } return res, nil } } return nil, ErrNoSuchInvite } func (st *AclState) ApplyRecord(record *AclRecord) (err error) { if st.lastRecordId != record.PrevId { err = ErrIncorrectRecordSequence return } // if the model is not cached if record.Model == nil { aclData := &aclrecordproto.AclData{} err = proto.Unmarshal(record.Data, aclData) if err != nil { return } record.Model = aclData } // applying records contents err = st.applyChangeData(record) if err != nil { return } // getting all states for users at record and saving them var states []AccountState for _, state := range st.accountStates { states = append(states, state) } st.lastRecordId = record.Id return } func (st *AclState) IsEmpty() bool { users := 0 for _, acc := range st.CurrentAccounts() { if !acc.Permissions.NoPermissions() { users++ } } return users == 1 && len(st.Invites()) == 0 && len(st.pendingRequests) == 0 } func (st *AclState) applyRoot(record *AclRecord) (err error) { root, ok := record.Model.(*aclrecordproto.AclRoot) if !ok { return ErrIncorrectRoot } if root.EncryptedReadKey != nil { mkPubKey, err := st.keyStore.PubKeyFromProto(root.MetadataPubKey) if err != nil { return err } st.keys[record.Id] = AclKeys{ MetadataPubKey: mkPubKey, encMetadatKey: root.EncryptedMetadataPrivKey, } } else { // this should be a derived acl st.keys[record.Id] = AclKeys{} } if st.key != nil && st.pubKey.Equals(record.Identity) { err = st.saveKeysFromRoot(record.Id, root) if err != nil { return } } // adding an account to the list accountState := AccountState{ PubKey: record.Identity, Permissions: AclPermissions(aclrecordproto.AclUserPermissions_Owner), KeyRecordId: record.Id, RequestMetadata: root.EncryptedOwnerMetadata, Status: StatusActive, PermissionChanges: []PermissionChange{ { RecordId: record.Id, Permission: AclPermissionsOwner, }, }, } st.readKeyChanges = []string{record.Id} st.accountStates[mapKeyFromPubKey(record.Identity)] = accountState st.lastRecordId = record.Id return } func (st *AclState) saveKeysFromRoot(id string, root *aclrecordproto.AclRoot) (err error) { aclKeys := st.keys[id] if root.EncryptedReadKey == nil { readKey, err := st.deriveKey() if err != nil { return err } aclKeys.ReadKey = readKey } else { readKey, err := st.unmarshallDecryptReadKey(root.EncryptedReadKey, st.key.Decrypt) if err != nil { return err } metadataKey, err := st.unmarshallDecryptPrivKey(root.EncryptedMetadataPrivKey, readKey.Decrypt) if err != nil { return err } aclKeys.ReadKey = readKey aclKeys.MetadataPrivKey = metadataKey } st.keys[id] = aclKeys return } func (st *AclState) applyChangeData(record *AclRecord) (err error) { model := record.Model.(*aclrecordproto.AclData) for _, ch := range model.GetAclContent() { if err = st.applyChangeContent(ch, record); err != nil { log.Info("error while applying changes", zap.Error(err)) return err } } return nil } func (st *AclState) Copy() *AclState { newSt := &AclState{ id: st.id, key: st.key, pubKey: st.key.GetPublic(), keys: make(map[string]AclKeys), accountStates: make(map[string]AccountState), invites: make(map[string]Invite), requestRecords: make(map[string]RequestRecord), pendingRequests: make(map[string]string), keyStore: st.keyStore, } for k, v := range st.keys { newSt.keys[k] = v } for k, v := range st.accountStates { var permChanges []PermissionChange permChanges = append(permChanges, v.PermissionChanges...) accState := v accState.PermissionChanges = permChanges newSt.accountStates[k] = accState } for k, v := range st.invites { newSt.invites[k] = v } for k, v := range st.requestRecords { newSt.requestRecords[k] = v } for k, v := range st.pendingRequests { newSt.pendingRequests[k] = v } newSt.readKeyChanges = append(newSt.readKeyChanges, st.readKeyChanges...) newSt.list = st.list newSt.lastRecordId = st.lastRecordId newSt.contentValidator = newContentValidator(newSt.keyStore, newSt, st.list.verifier) return newSt } func (st *AclState) applyChangeContent(ch *aclrecordproto.AclContentValue, record *AclRecord) error { switch { case ch.GetInviteJoin() != nil: return st.applyInviteJoin(ch.GetInviteJoin(), record) case ch.GetPermissionChange() != nil: return st.applyPermissionChange(ch.GetPermissionChange(), record) case ch.GetInvite() != nil: return st.applyInvite(ch.GetInvite(), record) case ch.GetInviteRevoke() != nil: return st.applyInviteRevoke(ch.GetInviteRevoke(), record) case ch.GetRequestJoin() != nil: return st.applyRequestJoin(ch.GetRequestJoin(), record) case ch.GetRequestAccept() != nil: return st.applyRequestAccept(ch.GetRequestAccept(), record) case ch.GetRequestDecline() != nil: return st.applyRequestDecline(ch.GetRequestDecline(), record) case ch.GetRequestCancel() != nil: return st.applyRequestCancel(ch.GetRequestCancel(), record) case ch.GetAccountRemove() != nil: return st.applyAccountRemove(ch.GetAccountRemove(), record) case ch.GetReadKeyChange() != nil: return st.applyReadKeyChange(ch.GetReadKeyChange(), record, true) case ch.GetAccountRequestRemove() != nil: return st.applyRequestRemove(ch.GetAccountRequestRemove(), record) case ch.GetAccountsAdd() != nil: return st.applyAccountsAdd(ch.GetAccountsAdd(), record) case ch.GetPermissionChanges() != nil: return st.applyPermissionChanges(ch.GetPermissionChanges(), record) default: log.Errorf("got unexpected content type: %s", record.Id) return nil } } func (st *AclState) applyPermissionChanges(ch *aclrecordproto.AclAccountPermissionChanges, record *AclRecord) (err error) { for _, ch := range ch.Changes { err := st.applyPermissionChange(ch, record) if err != nil { return err } } return nil } func (st *AclState) applyPermissionChange(ch *aclrecordproto.AclAccountPermissionChange, record *AclRecord) error { chIdentity, err := st.keyStore.PubKeyFromProto(ch.Identity) if err != nil { return err } err = st.contentValidator.ValidatePermissionChange(ch, record.Identity) if err != nil { return err } stringKey := mapKeyFromPubKey(chIdentity) state, _ := st.accountStates[stringKey] state.Permissions = AclPermissions(ch.Permissions) state.PermissionChanges = append(state.PermissionChanges, PermissionChange{ RecordId: record.Id, Permission: state.Permissions, }) st.accountStates[stringKey] = state return nil } func (st *AclState) applyInvite(ch *aclrecordproto.AclAccountInvite, record *AclRecord) error { inviteKey, err := st.keyStore.PubKeyFromProto(ch.InviteKey) if err != nil { return err } err = st.contentValidator.ValidateInvite(ch, record.Identity) if err != nil { return err } st.invites[record.Id] = Invite{ Key: inviteKey, Type: ch.InviteType, Permissions: AclPermissions(ch.Permissions), encryptedKey: ch.EncryptedReadKey, } return nil } func (st *AclState) applyInviteRevoke(ch *aclrecordproto.AclAccountInviteRevoke, record *AclRecord) error { err := st.contentValidator.ValidateInviteRevoke(ch, record.Identity) if err != nil { return err } delete(st.invites, ch.InviteRecordId) return nil } func (st *AclState) applyRequestJoin(ch *aclrecordproto.AclAccountRequestJoin, record *AclRecord) error { err := st.contentValidator.ValidateRequestJoin(ch, record.Identity) if err != nil { return err } st.pendingRequests[mapKeyFromPubKey(record.Identity)] = record.Id st.requestRecords[record.Id] = RequestRecord{ RequestIdentity: record.Identity, RequestMetadata: ch.Metadata, KeyRecordId: st.CurrentReadKeyId(), RecordId: record.Id, Type: RequestTypeJoin, } pKeyString := mapKeyFromPubKey(record.Identity) state, exists := st.accountStates[pKeyString] if !exists { st.accountStates[mapKeyFromPubKey(record.Identity)] = AccountState{ PubKey: record.Identity, Permissions: AclPermissionsNone, Status: StatusJoining, RequestMetadata: ch.Metadata, KeyRecordId: st.CurrentReadKeyId(), } } else { st.accountStates[mapKeyFromPubKey(record.Identity)] = AccountState{ PubKey: record.Identity, Permissions: AclPermissionsNone, Status: StatusJoining, RequestMetadata: ch.Metadata, KeyRecordId: st.CurrentReadKeyId(), PermissionChanges: state.PermissionChanges, } } return nil } func (st *AclState) applyAccountsAdd(ch *aclrecordproto.AclAccountsAdd, record *AclRecord) error { err := st.contentValidator.ValidateAccountsAdd(ch, record.Identity) if err != nil { return err } for _, acc := range ch.Additions { identity, err := st.keyStore.PubKeyFromProto(acc.Identity) if err != nil { return err } st.accountStates[mapKeyFromPubKey(identity)] = AccountState{ PubKey: identity, Permissions: AclPermissions(acc.Permissions), Status: StatusActive, RequestMetadata: acc.Metadata, KeyRecordId: st.CurrentReadKeyId(), PermissionChanges: []PermissionChange{ { Permission: AclPermissions(acc.Permissions), RecordId: record.Id, }, }, } if !st.pubKey.Equals(identity) { continue } err = st.unpackAllKeys(acc.EncryptedReadKey) if err != nil { return err } } return nil } func (st *AclState) applyRequestAccept(ch *aclrecordproto.AclAccountRequestAccept, record *AclRecord) error { err := st.contentValidator.ValidateRequestAccept(ch, record.Identity) if err != nil { return err } acceptIdentity, err := st.keyStore.PubKeyFromProto(ch.Identity) if err != nil { return err } requestRecord, _ := st.requestRecords[ch.RequestRecordId] pKeyString := mapKeyFromPubKey(acceptIdentity) state, exists := st.accountStates[pKeyString] if !exists { st.accountStates[pKeyString] = AccountState{ PubKey: acceptIdentity, Permissions: AclPermissions(ch.Permissions), RequestMetadata: requestRecord.RequestMetadata, KeyRecordId: requestRecord.KeyRecordId, Status: StatusActive, PermissionChanges: []PermissionChange{ { Permission: AclPermissions(ch.Permissions), RecordId: record.Id, }, }, } } else { st.accountStates[pKeyString] = AccountState{ PubKey: acceptIdentity, Permissions: AclPermissions(ch.Permissions), RequestMetadata: requestRecord.RequestMetadata, KeyRecordId: requestRecord.KeyRecordId, Status: StatusActive, PermissionChanges: append(state.PermissionChanges, PermissionChange{ Permission: AclPermissions(ch.Permissions), RecordId: record.Id, }), } } delete(st.pendingRequests, mapKeyFromPubKey(st.requestRecords[ch.RequestRecordId].RequestIdentity)) delete(st.requestRecords, ch.RequestRecordId) if !st.pubKey.Equals(acceptIdentity) { return nil } return st.unpackAllKeys(ch.EncryptedReadKey) } func (st *AclState) applyInviteJoin(ch *aclrecordproto.AclAccountInviteJoin, record *AclRecord) error { err := st.contentValidator.ValidateInviteJoin(ch, record.Identity) if err != nil { return err } identity, err := st.keyStore.PubKeyFromProto(ch.Identity) if err != nil { return err } inviteRecord, _ := st.invites[ch.InviteRecordId] pKeyString := mapKeyFromPubKey(identity) state, exists := st.accountStates[pKeyString] if !exists { st.accountStates[pKeyString] = AccountState{ PubKey: identity, Permissions: inviteRecord.Permissions, RequestMetadata: ch.Metadata, KeyRecordId: st.CurrentReadKeyId(), Status: StatusActive, PermissionChanges: []PermissionChange{ { Permission: inviteRecord.Permissions, RecordId: record.Id, }, }, } } else { st.accountStates[pKeyString] = AccountState{ PubKey: identity, Permissions: inviteRecord.Permissions, RequestMetadata: ch.Metadata, KeyRecordId: st.CurrentReadKeyId(), Status: StatusActive, PermissionChanges: append(state.PermissionChanges, PermissionChange{ Permission: inviteRecord.Permissions, RecordId: record.Id, }), } } if st.pubKey.Equals(identity) { return st.unpackAllKeys(ch.EncryptedReadKey) } return nil } func (st *AclState) unpackAllKeys(rk []byte) error { iterReadKey, err := st.unmarshallDecryptReadKey(rk, st.key.Decrypt) if err != nil { return err } for idx := len(st.readKeyChanges) - 1; idx >= 0; idx-- { recId := st.readKeyChanges[idx] keys := st.keys[recId] metadataKey, err := st.unmarshallDecryptPrivKey(keys.encMetadatKey, iterReadKey.Decrypt) if err != nil { return err } aclKeys := st.keys[recId] aclKeys.ReadKey = iterReadKey aclKeys.MetadataPrivKey = metadataKey st.keys[recId] = aclKeys if idx != 0 { if keys.oldEncryptedReadKey == nil { return ErrIncorrectReadKey } oldReadKey, err := st.unmarshallDecryptReadKey(keys.oldEncryptedReadKey, iterReadKey.Decrypt) if err != nil { return err } iterReadKey = oldReadKey } } return nil } func (st *AclState) applyRequestDecline(ch *aclrecordproto.AclAccountRequestDecline, record *AclRecord) error { err := st.contentValidator.ValidateRequestDecline(ch, record.Identity) if err != nil { return err } pk := mapKeyFromPubKey(st.requestRecords[ch.RequestRecordId].RequestIdentity) accSt, exists := st.accountStates[pk] if !exists { return ErrNoSuchAccount } accSt.Status = StatusDeclined st.accountStates[pk] = accSt delete(st.pendingRequests, mapKeyFromPubKey(st.requestRecords[ch.RequestRecordId].RequestIdentity)) delete(st.requestRecords, ch.RequestRecordId) return nil } func (st *AclState) applyRequestCancel(ch *aclrecordproto.AclAccountRequestCancel, record *AclRecord) error { err := st.contentValidator.ValidateRequestCancel(ch, record.Identity) if err != nil { return err } pk := mapKeyFromPubKey(st.requestRecords[ch.RecordId].RequestIdentity) accSt, exists := st.accountStates[pk] if !exists { return ErrNoSuchAccount } rec := st.requestRecords[ch.RecordId] if rec.Type == RequestTypeJoin { accSt.Status = StatusCanceled } else { accSt.Status = StatusActive } st.accountStates[pk] = accSt delete(st.pendingRequests, mapKeyFromPubKey(st.requestRecords[ch.RecordId].RequestIdentity)) delete(st.requestRecords, ch.RecordId) return nil } func (st *AclState) applyRequestRemove(ch *aclrecordproto.AclAccountRequestRemove, record *AclRecord) error { err := st.contentValidator.ValidateRequestRemove(ch, record.Identity) if err != nil { return err } st.requestRecords[record.Id] = RequestRecord{ RequestIdentity: record.Identity, Type: RequestTypeRemove, RecordId: record.Id, } st.pendingRequests[mapKeyFromPubKey(record.Identity)] = record.Id pk := mapKeyFromPubKey(record.Identity) accSt, exists := st.accountStates[pk] if !accSt.Permissions.CanRequestRemove() { return ErrInsufficientPermissions } if !exists { return ErrNoSuchAccount } accSt.Status = StatusRemoving st.accountStates[pk] = accSt return nil } func (st *AclState) applyAccountRemove(ch *aclrecordproto.AclAccountRemove, record *AclRecord) error { err := st.contentValidator.ValidateAccountRemove(ch, record.Identity) if err != nil { return err } for _, rawIdentity := range ch.Identities { identity, err := st.keyStore.PubKeyFromProto(rawIdentity) if err != nil { return err } idKey := mapKeyFromPubKey(identity) accSt, exists := st.accountStates[idKey] if !exists { return ErrNoSuchAccount } accSt.Status = StatusRemoved accSt.Permissions = AclPermissionsNone accSt.PermissionChanges = append(accSt.PermissionChanges, PermissionChange{ RecordId: record.Id, Permission: AclPermissionsNone, }) st.accountStates[idKey] = accSt recId, exists := st.pendingRequests[idKey] if exists { delete(st.pendingRequests, idKey) delete(st.requestRecords, recId) } } return st.applyReadKeyChange(ch.ReadKeyChange, record, false) } func (st *AclState) applyReadKeyChange(ch *aclrecordproto.AclReadKeyChange, record *AclRecord, validate bool) error { if validate { err := st.contentValidator.ValidateReadKeyChange(ch, record.Identity) if err != nil { return err } } st.readKeyChanges = append(st.readKeyChanges, record.Id) mkPubKey, err := st.keyStore.PubKeyFromProto(ch.MetadataPubKey) if err != nil { return err } aclKeys := AclKeys{ MetadataPubKey: mkPubKey, oldEncryptedReadKey: ch.EncryptedOldReadKey, encMetadatKey: ch.EncryptedMetadataPrivKey, } for _, accKey := range ch.AccountKeys { identity, _ := st.keyStore.PubKeyFromProto(accKey.Identity) if st.pubKey.Equals(identity) { res, err := st.unmarshallDecryptReadKey(accKey.EncryptedReadKey, st.key.Decrypt) if err != nil { return err } aclKeys.ReadKey = res } } for _, encKey := range ch.InviteKeys { invKey, err := st.keyStore.PubKeyFromProto(encKey.Identity) if err != nil { return err } for key, invite := range st.invites { if invite.Key.Equals(invKey) { invite.encryptedKey = encKey.EncryptedReadKey st.invites[key] = invite break } } } if aclKeys.ReadKey != nil { metadataKey, err := st.unmarshallDecryptPrivKey(ch.EncryptedMetadataPrivKey, aclKeys.ReadKey.Decrypt) if err != nil { return err } aclKeys.MetadataPrivKey = metadataKey } st.keys[record.Id] = aclKeys return nil } func (st *AclState) unmarshallDecryptReadKey(msg []byte, decryptor func(msg []byte) ([]byte, error)) (crypto.SymKey, error) { decrypted, err := decryptor(msg) if err != nil { return nil, ErrFailedToDecrypt } key, err := crypto.UnmarshallAESKeyProto(decrypted) if err != nil { return nil, ErrFailedToDecrypt } return key, nil } func (st *AclState) unmarshallDecryptPrivKey(msg []byte, decryptor func(msg []byte) ([]byte, error)) (crypto.PrivKey, error) { decrypted, err := decryptor(msg) if err != nil { return nil, ErrFailedToDecrypt } key, err := crypto.UnmarshalEd25519PrivateKeyProto(decrypted) if err != nil { return nil, ErrFailedToDecrypt } return key, nil } func (st *AclState) GetInviteIdByPrivKey(inviteKey crypto.PrivKey) (recId string, err error) { for id, inv := range st.invites { if inv.Key.Equals(inviteKey.GetPublic()) { return id, nil } } return "", ErrNoSuchRecord } func (st *AclState) GetMetadata(identity crypto.PubKey, decrypt bool) (res []byte, err error) { state, exists := st.accountStates[mapKeyFromPubKey(identity)] if !exists { return nil, ErrNoSuchAccount } if !decrypt { return state.RequestMetadata, nil } aclKeys := st.keys[state.KeyRecordId] if aclKeys.MetadataPrivKey == nil { return nil, ErrFailedToDecrypt } return aclKeys.MetadataPrivKey.Decrypt(state.RequestMetadata) } func (st *AclState) Permissions(identity crypto.PubKey) AclPermissions { state, exists := st.accountStates[mapKeyFromPubKey(identity)] if !exists { return AclPermissions(aclrecordproto.AclUserPermissions_None) } return state.Permissions } func (st *AclState) JoinRecords(decrypt bool) (records []RequestRecord, err error) { for _, recId := range st.pendingRequests { rec := st.requestRecords[recId] if rec.Type == RequestTypeJoin { if decrypt { aclKeys := st.keys[rec.KeyRecordId] if aclKeys.MetadataPrivKey == nil { return nil, ErrFailedToDecrypt } res, err := aclKeys.MetadataPrivKey.Decrypt(rec.RequestMetadata) if err != nil { return nil, err } rec.RequestMetadata = res } records = append(records, rec) } } return } func (st *AclState) Record(identity crypto.PubKey) (RequestRecord, error) { recId, exists := st.pendingRequests[mapKeyFromPubKey(identity)] if !exists { return RequestRecord{}, ErrNoSuchRecord } return st.requestRecords[recId], nil } func (st *AclState) JoinRecord(identity crypto.PubKey, decrypt bool) (RequestRecord, error) { recId, exists := st.pendingRequests[mapKeyFromPubKey(identity)] if !exists { return RequestRecord{}, ErrNoSuchRecord } rec := st.requestRecords[recId] if decrypt { aclKeys := st.keys[rec.KeyRecordId] if aclKeys.MetadataPrivKey == nil { return RequestRecord{}, ErrFailedToDecrypt } res, err := aclKeys.MetadataPrivKey.Decrypt(rec.RequestMetadata) if err != nil { return RequestRecord{}, err } rec.RequestMetadata = res } return rec, nil } func (st *AclState) RemoveRecords() (records []RequestRecord) { for _, recId := range st.pendingRequests { rec := st.requestRecords[recId] if rec.Type == RequestTypeRemove { records = append(records, rec) } } return } func (st *AclState) LastRecordId() string { return st.lastRecordId } func (st *AclState) OwnerPubKey() (ownerIdentity crypto.PubKey, err error) { for _, aState := range st.accountStates { if aState.Permissions.IsOwner() { return aState.PubKey, nil } } return nil, ErrOwnerNotFound } func (st *AclState) deriveKey() (crypto.SymKey, error) { keyBytes, err := st.key.Raw() if err != nil { return nil, err } return crypto.DeriveSymmetricKey(keyBytes, crypto.AnysyncSpacePath) } func mapKeyFromPubKey(pubKey crypto.PubKey) string { return string(pubKey.Storage()) } func closestPermissions(accountState AccountState, recordId string, isAfter func(first, second string) bool) AclPermissions { if len(accountState.PermissionChanges) == 0 { return AclPermissionsNone } permChanges := accountState.PermissionChanges for i := len(permChanges) - 1; i >= 0; i-- { if isAfter(recordId, permChanges[i].RecordId) { return permChanges[i].Permission } } return AclPermissionsNone }