From 23ef09bba7cd7429252ef78635015a9b5a3aa14f Mon Sep 17 00:00:00 2001 From: Guillaume Louvigny Date: Wed, 22 Mar 2023 10:13:18 +0100 Subject: [PATCH] fix: unifying multiple secret stores into a single interface Signed-off-by: Guillaume Louvigny --- account_export.go | 62 +- account_export_test.go | 84 +- api/protocol/protocoltypes.proto | 67 +- api/protocol/pushtypes/pushtypes.proto | 2 +- api_contactrequest.go | 7 +- api_debug.go | 12 +- api_group.go | 6 +- api_multimember.go | 14 +- api_push.go | 241 +- api_push_test.go | 246 -- api_replication.go | 3 +- api_services_auth.go | 36 +- api_verified_credentials.go | 31 +- blackbox_test.go | 11 +- consts.go | 1 - contact_request_manager.go | 30 +- docs/apis/protocoltypes.md | 193 +- docs/apis/protocoltypes.swagger.json | 89 +- events.go | 8 +- gen.sum | 2 +- group.go | 95 +- group_context.go | 65 +- group_test.go | 36 - iface_account.go | 34 +- internal/datastoreutil/consts.go | 4 +- message_marshaler.go | 12 +- message_marshaler_test.go | 27 +- orbitdb.go | 80 +- orbitdb_many_adds_berty_test.go | 8 +- orbitdb_utils_test.go | 2 +- pkg/bertypush/push_handler.go | 290 -- pkg/cryptoutil/group.go | 85 - pkg/cryptoutil/group_store.go | 213 -- pkg/cryptoutil/keystore_device.go | 327 -- pkg/cryptoutil/keystore_device_test.go | 175 -- pkg/cryptoutil/keystore_message.go | 918 ------ pkg/cryptoutil/keystore_message_utils.go | 159 - pkg/cryptoutil/keystore_message_utils_test.go | 616 ---- pkg/cryptoutil/signer_wrapper.go | 28 + pkg/protocoltypes/group.go | 89 + pkg/protocoltypes/protocoltypes.pb.go | 2773 ++++------------- pkg/protocoltypes/protocoltypes.pb.gw.go | 299 +- pkg/protocoltypes/protocoltypes_grpc.pb.go | 178 +- pkg/pushtypes/pushtypes.pb.go | 200 +- pkg/secretstore/chain_key.go | 87 + pkg/secretstore/datastore_keys.go | 117 + pkg/secretstore/device_keystore_wrapper.go | 259 ++ .../device_keystore_wrapper_test.go | 207 ++ pkg/secretstore/doc.go | 2 + pkg/secretstore/keys_utils.go | 147 + pkg/secretstore/member_device.go | 62 + pkg/secretstore/secret_store.go | 379 +++ pkg/secretstore/secret_store_interfaces.go | 152 + pkg/secretstore/secret_store_messages.go | 1067 +++++++ pkg/secretstore/secret_store_messages_test.go | 244 ++ pkg/secretstore/secret_store_test.go | 567 ++++ scenario_test.go | 7 +- service.go | 126 +- service_group.go | 50 +- store_message.go | 238 +- store_message_queue.go | 2 - store_message_test.go | 12 +- store_metadata.go | 246 +- store_metadata_index.go | 104 +- store_metadata_test.go | 37 +- testing.go | 153 +- 66 files changed, 4897 insertions(+), 7226 deletions(-) delete mode 100644 api_push_test.go delete mode 100644 group_test.go delete mode 100644 pkg/bertypush/push_handler.go delete mode 100644 pkg/cryptoutil/group.go delete mode 100644 pkg/cryptoutil/group_store.go delete mode 100644 pkg/cryptoutil/keystore_device.go delete mode 100644 pkg/cryptoutil/keystore_device_test.go delete mode 100644 pkg/cryptoutil/keystore_message.go delete mode 100644 pkg/cryptoutil/keystore_message_utils.go delete mode 100644 pkg/cryptoutil/keystore_message_utils_test.go create mode 100644 pkg/cryptoutil/signer_wrapper.go create mode 100644 pkg/secretstore/chain_key.go create mode 100644 pkg/secretstore/datastore_keys.go create mode 100644 pkg/secretstore/device_keystore_wrapper.go create mode 100644 pkg/secretstore/device_keystore_wrapper_test.go create mode 100644 pkg/secretstore/doc.go create mode 100644 pkg/secretstore/keys_utils.go create mode 100644 pkg/secretstore/member_device.go create mode 100644 pkg/secretstore/secret_store.go create mode 100644 pkg/secretstore/secret_store_interfaces.go create mode 100644 pkg/secretstore/secret_store_messages.go create mode 100644 pkg/secretstore/secret_store_messages_test.go create mode 100644 pkg/secretstore/secret_store_test.go diff --git a/account_export.go b/account_export.go index b76ccce2..434b2c99 100644 --- a/account_export.go +++ b/account_export.go @@ -12,14 +12,11 @@ import ( "github.com/ipfs/go-cid" cbornode "github.com/ipfs/go-ipld-cbor" ipfs_interface "github.com/ipfs/interface-go-ipfs-core" - "github.com/libp2p/go-libp2p/core/crypto" - crypto_pb "github.com/libp2p/go-libp2p/core/crypto/pb" mh "github.com/multiformats/go-multihash" "go.uber.org/multierr" "go.uber.org/zap" orbitdb "berty.tech/go-orbit-db" - "berty.tech/weshnet/pkg/cryptoutil" "berty.tech/weshnet/pkg/errcode" "berty.tech/weshnet/pkg/protocoltypes" ) @@ -35,11 +32,7 @@ func (s *service) export(ctx context.Context, output io.Writer) error { tw := tar.NewWriter(output) defer tw.Close() - if err := s.exportAccountKey(tw); err != nil { - return errcode.ErrInternal.Wrap(err) - } - - if err := s.exportAccountProofKey(tw); err != nil { + if err := s.exportAccountKeys(tw); err != nil { return errcode.ErrInternal.Wrap(err) } @@ -109,22 +102,23 @@ func (s *service) exportOrbitDBStore(ctx context.Context, store orbitdb.Store, t return nil } -func (s *service) exportAccountKey(tw *tar.Writer) error { - sk, err := s.deviceKeystore.AccountPrivKey() +func (s *service) exportAccountKeys(tw *tar.Writer) error { + accountPrivateKeyBytes, accountProofPrivateKeyBytes, err := s.secretStore.ExportAccountKeysForBackup() if err != nil { return errcode.ErrInternal.Wrap(err) } - return exportPrivateKey(tw, sk, exportAccountKeyFilename) -} + err = exportPrivateKey(tw, accountPrivateKeyBytes, exportAccountKeyFilename) + if err != nil { + return errcode.ErrStreamWrite.Wrap(err) + } -func (s *service) exportAccountProofKey(tw *tar.Writer) error { - sk, err := s.deviceKeystore.AccountProofPrivKey() + err = exportPrivateKey(tw, accountProofPrivateKeyBytes, exportAccountProofKeyFilename) if err != nil { - return errcode.ErrInternal.Wrap(err) + return errcode.ErrStreamWrite.Wrap(err) } - return exportPrivateKey(tw, sk, exportAccountProofKeyFilename) + return nil } func (s *service) exportOrbitDBGroupHeads(gc *GroupContext, headsMetadata []cid.Cid, headsMessages []cid.Cid, tw *tar.Writer) error { @@ -148,7 +142,7 @@ func (s *service) exportOrbitDBGroupHeads(gc *GroupContext, headsMetadata []cid. return errcode.ErrSerialization.Wrap(err) } - linkKeyArr, err := cryptoutil.GetLinkKeyArray(gc.group) + linkKeyArr, err := gc.group.GetLinkKeyArray() if err != nil { return errcode.ErrSerialization.Wrap(err) } @@ -189,28 +183,23 @@ func (s *service) exportOrbitDBGroupHeads(gc *GroupContext, headsMetadata []cid. return nil } -func exportPrivateKey(tw *tar.Writer, sk crypto.PrivKey, filename string) error { - skBytes, err := crypto.MarshalPrivateKey(sk) - if err != nil { - return errcode.ErrSerialization.Wrap(err) - } - +func exportPrivateKey(tw *tar.Writer, marshalledPrivateKey []byte, filename string) error { if err := tw.WriteHeader(&tar.Header{ Typeflag: tar.TypeReg, Name: filename, Mode: 0o600, - Size: int64(len(skBytes)), + Size: int64(len(marshalledPrivateKey)), }); err != nil { return errcode.ErrStreamWrite.Wrap(err) } - size, err := tw.Write(skBytes) + size, err := tw.Write(marshalledPrivateKey) if err != nil { return errcode.ErrStreamWrite.Wrap(err) } - if size != len(skBytes) { - return errcode.ErrStreamWrite.Wrap(fmt.Errorf("wrote %d bytes instead of %d", size, len(skBytes))) + if size != len(marshalledPrivateKey) { + return errcode.ErrStreamWrite.Wrap(fmt.Errorf("wrote %d bytes instead of %d", size, len(marshalledPrivateKey))) } return nil @@ -250,7 +239,7 @@ func (s *service) exportOrbitDBEntry(ctx context.Context, tw *tar.Writer, idStr return nil } -func readExportSecretKeyFile(expectedSize int64, reader *tar.Reader) (crypto.PrivKey, error) { +func readExportSecretKeyFile(expectedSize int64, reader *tar.Reader) ([]byte, error) { if expectedSize == 0 { return nil, errcode.ErrInvalidInput.Wrap(fmt.Errorf("invalid expected key size")) } @@ -265,16 +254,7 @@ func readExportSecretKeyFile(expectedSize int64, reader *tar.Reader) (crypto.Pri return nil, errcode.ErrInternal.Wrap(fmt.Errorf("unexpected file size")) } - sk, err := crypto.UnmarshalPrivateKey(keyContents.Bytes()) - if err != nil { - return nil, errcode.ErrDeserialization.Wrap(fmt.Errorf("unable to unmarshal private key")) - } - - if sk.Type() != crypto_pb.KeyType_Ed25519 { - return nil, errcode.ErrInvalidInput.Wrap(fmt.Errorf("invalid key format")) - } - - return sk, nil + return keyContents.Bytes(), nil } func readExportOrbitDBGroupHeads(expectedSize int64, reader *tar.Reader) (*protocoltypes.GroupHeadsExport, []cid.Cid, []cid.Cid, error) { @@ -354,7 +334,7 @@ type RestoreAccountHandler struct { } type restoreAccountState struct { - keys map[string]crypto.PrivKey + keys map[string][]byte } func (state *restoreAccountState) readKey(keyName string) RestoreAccountHandler { @@ -383,7 +363,7 @@ func (state *restoreAccountState) readKey(keyName string) RestoreAccountHandler func (state *restoreAccountState) restoreKeys(odb *WeshOrbitDB) RestoreAccountHandler { return RestoreAccountHandler{ PostProcess: func() error { - if err := odb.deviceKeystore.RestoreAccountKeys(state.keys[exportAccountKeyFilename], state.keys[exportAccountProofKeyFilename]); err != nil { + if err := odb.secretStore.ImportAccountKeys(state.keys[exportAccountKeyFilename], state.keys[exportAccountProofKeyFilename]); err != nil { return errcode.ErrInternal.Wrap(err) } @@ -443,7 +423,7 @@ func restoreOrbitDBHeads(ctx context.Context, odb *WeshOrbitDB) RestoreAccountHa func RestoreAccountExport(ctx context.Context, reader io.Reader, coreAPI ipfs_interface.CoreAPI, odb *WeshOrbitDB, logger *zap.Logger, handlers ...RestoreAccountHandler) error { tr := tar.NewReader(reader) state := restoreAccountState{ - keys: map[string]crypto.PrivKey{}, + keys: map[string][]byte{}, } handlers = append( diff --git a/account_export_test.go b/account_export_test.go index 59459dad..9a22e05b 100644 --- a/account_export_test.go +++ b/account_export_test.go @@ -11,21 +11,19 @@ import ( "github.com/ipfs/go-cid" ds "github.com/ipfs/go-datastore" dsync "github.com/ipfs/go-datastore/sync" - "github.com/libp2p/go-libp2p/core/crypto" mocknet "github.com/libp2p/go-libp2p/p2p/net/mock" "github.com/stretchr/testify/require" orbitdb "berty.tech/go-orbit-db" "berty.tech/go-orbit-db/pubsub/pubsubraw" - "berty.tech/weshnet/internal/datastoreutil" - "berty.tech/weshnet/pkg/cryptoutil" "berty.tech/weshnet/pkg/ipfsutil" "berty.tech/weshnet/pkg/protocoltypes" + "berty.tech/weshnet/pkg/secretstore" "berty.tech/weshnet/pkg/testutil" "berty.tech/weshnet/pkg/tinder" ) -func Test_service_exportAccountKey(t *testing.T) { +func Test_service_exportAccountKeys(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -53,7 +51,7 @@ func Test_service_exportAccountKey(t *testing.T) { tw := tar.NewWriter(tmpFile) - err = s.exportAccountKey(tw) + err = s.exportAccountKeys(tw) require.NoError(t, err) err = tw.Close() @@ -63,75 +61,30 @@ func Test_service_exportAccountKey(t *testing.T) { require.NoError(t, err) tr := tar.NewReader(tmpFile) - header, err := tr.Next() - require.NoError(t, err) - require.Equal(t, exportAccountKeyFilename, header.Name) - - keyContents := make([]byte, header.Size) - - size, err := tr.Read(keyContents) - require.Equal(t, int(header.Size), size) - sk, err := crypto.UnmarshalPrivateKey(keyContents) - require.NoError(t, err) + accountPrivateKey := getKeyFromTar(t, tr, exportAccountKeyFilename) + accountProofPrivateKey := getKeyFromTar(t, tr, exportAccountProofKeyFilename) - accountSK, err := s.deviceKeystore.AccountPrivKey() + inStoreAccountPrivateKeyBytes, inStoreAccountProofPrivateKeyBytes, err := s.secretStore.ExportAccountKeysForBackup() require.NoError(t, err) + require.NotNil(t, inStoreAccountPrivateKeyBytes) + require.NotNil(t, inStoreAccountProofPrivateKeyBytes) - require.True(t, accountSK.Equals(sk)) + require.Equal(t, accountPrivateKey, inStoreAccountPrivateKeyBytes) + require.Equal(t, accountProofPrivateKey, inStoreAccountProofPrivateKeyBytes) } -func Test_service_exportAccountProofKey(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - mn := mocknet.New() - defer mn.Close() - - msrv := tinder.NewMockDriverServer() - - dsA := dsync.MutexWrap(ds.NewMapDatastore()) - nodeA, closeNodeA := NewTestingProtocol(ctx, t, &TestingOpts{ - Mocknet: mn, - DiscoveryServer: msrv, - }, dsA) - defer closeNodeA() - - s, ok := nodeA.Service.(*service) - require.True(t, ok) - - tmpFile, err := ioutil.TempFile(os.TempDir(), "test-export-") - require.NoError(t, err) - - defer os.Remove(tmpFile.Name()) - - tw := tar.NewWriter(tmpFile) - err = s.exportAccountProofKey(tw) - require.NoError(t, err) - - err = tw.Close() - require.NoError(t, err) - - _, err = tmpFile.Seek(0, io.SeekStart) - require.NoError(t, err) - - tr := tar.NewReader(tmpFile) +func getKeyFromTar(t *testing.T, tr *tar.Reader, expectedFilename string) []byte { header, err := tr.Next() require.NoError(t, err) - require.Equal(t, exportAccountProofKeyFilename, header.Name) + require.Equal(t, expectedFilename, header.Name) keyContents := make([]byte, header.Size) size, err := tr.Read(keyContents) require.Equal(t, int(header.Size), size) - sk, err := crypto.UnmarshalPrivateKey(keyContents) - require.NoError(t, err) - - accountProofSK, err := s.deviceKeystore.AccountProofPrivKey() - require.NoError(t, err) - - require.True(t, accountProofSK.Equals(sk)) + return keyContents } func TestRestoreAccount(t *testing.T) { @@ -215,20 +168,21 @@ func TestRestoreAccount(t *testing.T) { { dsB := dsync.MutexWrap(ds.NewMapDatastore()) + secretStoreB, err := secretstore.NewSecretStore(dsB, nil) + require.NoError(t, err) + ipfsNodeB := ipfsutil.TestingCoreAPIUsingMockNet(ctx, t, &ipfsutil.TestingAPIOpts{ Mocknet: mn, Datastore: dsB, }) - dksB := cryptoutil.NewDeviceKeystore(ipfsutil.NewDatastoreKeystore(datastoreutil.NewNamespacedDatastore(dsB, ds.NewKey(NamespaceDeviceKeystore))), nil) - odb, err := NewWeshOrbitDB(ctx, ipfsNodeB.API(), &NewOrbitDBOptions{ NewOrbitDBOptions: orbitdb.NewOrbitDBOptions{ PubSub: pubsubraw.NewPubSub(ipfsNodeB.PubSub(), ipfsNodeB.MockNode().PeerHost.ID(), logger, nil), Logger: logger, }, - Datastore: dsB, - DeviceKeystore: dksB, + Datastore: dsB, + SecretStore: secretStoreB, }) require.NoError(t, err) @@ -238,7 +192,7 @@ func TestRestoreAccount(t *testing.T) { nodeB, closeNodeB := NewTestingProtocol(ctx, t, &TestingOpts{ Mocknet: mn, DiscoveryServer: msrv, - DeviceKeystore: dksB, + SecretStore: secretStoreB, CoreAPIMock: ipfsNodeB, OrbitDB: odb, }, dsB) diff --git a/api/protocol/protocoltypes.proto b/api/protocol/protocoltypes.proto index 5b313412..4763c9ec 100644 --- a/api/protocol/protocoltypes.proto +++ b/api/protocol/protocoltypes.proto @@ -127,20 +127,11 @@ service ProtocolService { // PeerList returns a list of P2P peers rpc PeerList(PeerList.Request) returns (PeerList.Reply); - // PushReceive handles a push payload, decrypts it if possible - rpc PushReceive(PushReceive.Request) returns (PushReceive.Reply); + // OutOfStoreReceive parses a payload received outside a synchronized store + rpc OutOfStoreReceive(OutOfStoreReceive.Request) returns (OutOfStoreReceive.Reply); - // PushSend sends a push payload to a specified list of group members - rpc PushSend(PushSend.Request) returns (PushSend.Reply); - - // PushShareToken sends push tokens of own devices to a group - rpc PushShareToken(PushShareToken.Request) returns (PushShareToken.Reply); - - // PushSetDeviceToken registers a push token for the current device - rpc PushSetDeviceToken(PushSetDeviceToken.Request) returns (PushSetDeviceToken.Reply); - - // PushSetServer registers a push server for the current device - rpc PushSetServer(PushSetServer.Request) returns (PushSetServer.Reply); + // OutOfStoreSeal creates a payload of a message present in store to be sent outside a synchronized store + rpc OutOfStoreSeal(OutOfStoreSeal.Request) returns (OutOfStoreSeal.Reply); // RefreshContactRequest try to refresh the contact request for the given contact rpc RefreshContactRequest(RefreshContactRequest.Request) returns (RefreshContactRequest.Reply); @@ -175,8 +166,8 @@ enum EventType { // EventTypeGroupMemberDeviceAdded indicates the payload includes that a member has added their device to the group EventTypeGroupMemberDeviceAdded = 1; - // EventTypeGroupDeviceSecretAdded indicates the payload includes that a member has sent their device secret to another member - EventTypeGroupDeviceSecretAdded = 2; + // EventTypeGroupDeviceChainKeyAdded indicates the payload includes that a member has sent their device chain key to another member + EventTypeGroupDeviceChainKeyAdded = 2; // EventTypeGroupAdditionalRendezvousSeedAdded adds a new rendezvous seed to a group // Might be implemented later, could be useful for replication services @@ -285,7 +276,7 @@ message Group { // secret_sig is the signature of the secret used to ensure the validity of the group bytes secret_sig = 3; - // group_type specifies the type of the group, used to determine how device secrets are generated + // group_type specifies the type of the group, used to determine how device chain key is generated GroupType group_type = 4; // sign_pub is the signature public key used to verify entries, not required when secret and secret_sig are provided @@ -423,7 +414,7 @@ message ContactAddAliasKey { } // GroupAddMemberDevice is an event which indicates to a group a new device (and eventually a new member) is joining it -// When added on AccountGroup, this event should be followed by appropriate GroupAddMemberDevice and GroupAddDeviceSecret events +// When added on AccountGroup, this event should be followed by appropriate GroupAddMemberDevice and GroupAddDeviceChainKey events message GroupAddMemberDevice { // member_pk is the member sending the event bytes member_pk = 1 [(gogoproto.customname) = "MemberPK"]; @@ -435,8 +426,8 @@ message GroupAddMemberDevice { bytes member_sig = 3; // TODO: signature of what ??? ensure it can't be replayed } -// DeviceSecret is encrypted for a specific member of the group -message DeviceSecret { +// DeviceChainKey is a chain key, which will be encrypted for a specific member of the group +message DeviceChainKey { // chain_key is the current value of the chain key of the group device bytes chain_key = 1; @@ -444,8 +435,8 @@ message DeviceSecret { uint64 counter = 2; } -// GroupAddDeviceSecret is an event which indicates to a group member a device secret -message GroupAddDeviceSecret { +// GroupAddDeviceChainKey is an event which indicates to a group member a device chain key +message GroupAddDeviceChainKey { // device_pk is the device sending the event, signs the message bytes device_pk = 1 [(gogoproto.customname) = "DevicePK"]; @@ -544,7 +535,7 @@ message AccountContactRequestReferenceReset { // This event should be followed by an AccountGroupJoined event // This event should be followed by a GroupAddMemberDevice event within the AccountGroup -// This event should be followed by a GroupAddDeviceSecret event within the AccountGroup +// This event should be followed by a GroupAddDeviceChainKey event within the AccountGroup // AccountContactRequestEnqueued indicates that the account will attempt to send a contact request when a matching peer is discovered message AccountContactRequestEnqueued { // device_pk is the device sending the event, signs the message @@ -596,7 +587,7 @@ message AccountContactRequestDiscarded { } // This event should be followed by an AccountGroupJoined event -// This event should be followed by GroupAddMemberDevice and GroupAddDeviceSecret events within the AccountGroup +// This event should be followed by GroupAddMemberDevice and GroupAddDeviceChainKey events within the AccountGroup // AccountContactRequestAccepted indicates that a contact request has been accepted message AccountContactRequestAccepted { // device_pk is the device sending the event, signs the message @@ -1436,7 +1427,7 @@ message PushMemberTokenUpdate { bytes device_pk = 3 [(gogoproto.customname) = "DevicePK"]; } -message PushReceive { +message OutOfStoreReceive { message Request { bytes payload = 1; } @@ -1448,38 +1439,14 @@ message PushReceive { } } -message PushSend { +message OutOfStoreSeal { message Request { bytes cid = 1 [(gogoproto.customname) = "CID"]; bytes group_public_key = 2; - repeated MemberWithDevices group_members = 3; } message Reply { - repeated MemberWithDevices group_members = 1; - } -} - -message PushShareToken { - message Request { - bytes group_pk = 1 [(gogoproto.customname) = "GroupPK"]; - PushServer server = 2; - PushServiceReceiver receiver = 3; + bytes encrypted = 1; } - message Reply {} -} - -message PushSetDeviceToken { - message Request { - PushServiceReceiver receiver = 1; - } - message Reply {} -} - -message PushSetServer { - message Request { - PushServer server = 1; - } - message Reply {} } message FirstLastCounters { diff --git a/api/protocol/pushtypes/pushtypes.proto b/api/protocol/pushtypes/pushtypes.proto index 65d42f7f..3d5e8285 100644 --- a/api/protocol/pushtypes/pushtypes.proto +++ b/api/protocol/pushtypes/pushtypes.proto @@ -77,7 +77,7 @@ message OutOfStoreMessageEnvelope { bytes group_reference = 4 [(gogoproto.customname) = "GroupReference"]; } -message PushExposedData { +message OutOfStoreExposedData { bytes nonce = 1; bytes box = 2; } diff --git a/api_contactrequest.go b/api_contactrequest.go index 89eed3ad..e6b960d8 100644 --- a/api_contactrequest.go +++ b/api_contactrequest.go @@ -133,6 +133,11 @@ func (s *service) ContactRequestAccept(ctx context.Context, req *protocoltypes.C return nil, errcode.ErrDeserialization.Wrap(err) } + group, err := s.secretStore.GetGroupForContact(pk) + if err != nil { + return nil, errcode.ErrInternal.Wrap(err) + } + accountGroup := s.getAccountGroup() if accountGroup == nil { return nil, errcode.ErrGroupMissing @@ -142,7 +147,7 @@ func (s *service) ContactRequestAccept(ctx context.Context, req *protocoltypes.C return nil, errcode.ErrOrbitDBAppend.Wrap(err) } - if err = s.groupDatastore.PutForContactPK(ctx, pk, s.deviceKeystore); err != nil { + if err = s.secretStore.PutGroup(ctx, group); err != nil { return nil, err } diff --git a/api_debug.go b/api_debug.go index 223ee37a..ae002ffb 100644 --- a/api_debug.go +++ b/api_debug.go @@ -15,7 +15,6 @@ import ( "berty.tech/go-orbit-db/stores/operation" "berty.tech/weshnet/internal/sysutil" - "berty.tech/weshnet/pkg/cryptoutil" "berty.tech/weshnet/pkg/errcode" "berty.tech/weshnet/pkg/protocoltypes" ) @@ -39,19 +38,14 @@ func (s *service) DebugListGroups(req *protocoltypes.DebugListGroups_Request, sr return errcode.ErrDeserialization.Wrap(err) } - sk, err := s.deviceKeystore.ContactGroupPrivKey(pk) + group, err := s.secretStore.GetGroupForContact(pk) if err != nil { return errcode.ErrCryptoKeyGeneration.Wrap(err) } - g, err := cryptoutil.GetGroupForContact(sk) - if err != nil { - return errcode.ErrOrbitDBOpen.Wrap(err) - } - if err := srv.SendMsg(&protocoltypes.DebugListGroups_Reply{ - GroupPK: g.PublicKey, - GroupType: g.GroupType, + GroupPK: group.PublicKey, + GroupType: group.GroupType, ContactPK: c.PK, }); err != nil { return err diff --git a/api_group.go b/api_group.go index e1ee115f..d4fd77be 100644 --- a/api_group.go +++ b/api_group.go @@ -47,17 +47,17 @@ func (s *service) GroupInfo(ctx context.Context, req *protocoltypes.GroupInfo_Re return nil, errcode.ErrInvalidInput } - md, err := s.deviceKeystore.MemberDeviceForGroup(g) + memberDevice, err := s.secretStore.GetOwnMemberDeviceForGroup(g) if err != nil { return nil, errcode.TODO.Wrap(err) } - member, err := md.PrivateMember().GetPublic().Raw() + member, err := memberDevice.Member().Raw() if err != nil { return nil, errcode.ErrSerialization.Wrap(err) } - device, err := md.PrivateDevice().GetPublic().Raw() + device, err := memberDevice.Device().Raw() if err != nil { return nil, errcode.ErrSerialization.Wrap(err) } diff --git a/api_multimember.go b/api_multimember.go index cf5daadd..699fc8de 100644 --- a/api_multimember.go +++ b/api_multimember.go @@ -16,7 +16,7 @@ func (s *service) MultiMemberGroupCreate(ctx context.Context, req *protocoltypes ctx, _, endSection := tyber.Section(ctx, s.logger, "Creating MultiMember group") defer func() { endSection(err, "") }() - g, sk, err := NewGroupMultiMember() + group, groupPrivateKey, err := NewGroupMultiMember() if err != nil { return nil, errcode.ErrCryptoKeyGeneration.Wrap(err) } @@ -26,32 +26,32 @@ func (s *service) MultiMemberGroupCreate(ctx context.Context, req *protocoltypes return nil, errcode.ErrGroupMissing } - _, err = accountGroup.MetadataStore().GroupJoin(ctx, g) + _, err = accountGroup.MetadataStore().GroupJoin(ctx, group) if err != nil { return nil, errcode.ErrOrbitDBAppend.Wrap(err) } - if err := s.groupDatastore.Put(ctx, g); err != nil { + if err := s.secretStore.PutGroup(ctx, group); err != nil { return nil, errcode.ErrInternal.Wrap(err) } - err = s.activateGroup(ctx, sk.GetPublic(), false) + err = s.activateGroup(ctx, groupPrivateKey.GetPublic(), false) if err != nil { return nil, errcode.ErrInternal.Wrap(fmt.Errorf("unable to activate group: %w", err)) } - cg, err := s.GetContextGroupForID(g.PublicKey) + cg, err := s.GetContextGroupForID(group.PublicKey) if err != nil { return nil, errcode.ErrOrbitDBAppend.Wrap(err) } - _, err = cg.MetadataStore().ClaimGroupOwnership(ctx, sk) + _, err = cg.MetadataStore().ClaimGroupOwnership(ctx, groupPrivateKey) if err != nil { return nil, errcode.ErrOrbitDBAppend.Wrap(err) } return &protocoltypes.MultiMemberGroupCreate_Reply{ - GroupPK: g.PublicKey, + GroupPK: group.PublicKey, }, nil } diff --git a/api_push.go b/api_push.go index 144e4f95..83919fde 100644 --- a/api_push.go +++ b/api_push.go @@ -1,59 +1,23 @@ package weshnet import ( - "bytes" "context" - crand "crypto/rand" "crypto/tls" - "encoding/base64" - "fmt" - "sync" "time" "github.com/ipfs/go-cid" - "github.com/libp2p/go-libp2p/core/crypto" "go.uber.org/zap" - "golang.org/x/crypto/nacl/box" "google.golang.org/grpc" "google.golang.org/grpc/backoff" "google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials/insecure" - "berty.tech/weshnet/pkg/cryptoutil" "berty.tech/weshnet/pkg/errcode" "berty.tech/weshnet/pkg/logutil" "berty.tech/weshnet/pkg/protocoltypes" "berty.tech/weshnet/pkg/pushtypes" ) -func PushSealTokenForServer(receiver *protocoltypes.PushServiceReceiver, server *protocoltypes.PushServer) (*protocoltypes.PushMemberTokenUpdate, error) { - if server == nil || len(server.ServerKey) != cryptoutil.KeySize { - return nil, errcode.ErrInvalidInput.Wrap(fmt.Errorf("expected a server key of %d bytes", cryptoutil.KeySize)) - } - - if receiver == nil { - return nil, errcode.ErrInvalidInput.Wrap(fmt.Errorf("expected the receiver value to be defined")) - } - - serverKey := [cryptoutil.KeySize]byte{} - copy(serverKey[:], server.ServerKey) - - opaqueToken, err := receiver.Marshal() - if err != nil { - return nil, errcode.ErrSerialization.Wrap(err) - } - - opaqueToken, err = box.SealAnonymous(nil, opaqueToken, &serverKey, crand.Reader) - if err != nil { - return nil, err - } - - return &protocoltypes.PushMemberTokenUpdate{ - Token: opaqueToken, - Server: server, - }, nil -} - func (s *service) getPushClient(host string) (pushtypes.PushServiceClient, error) { s.muPushClients.Lock() defer s.muPushClients.Unlock() @@ -95,11 +59,21 @@ func (s *service) getPushClient(host string) (pushtypes.PushServiceClient, error return pushtypes.NewPushServiceClient(cc), err } -func (s *service) PushReceive(ctx context.Context, request *protocoltypes.PushReceive_Request) (*protocoltypes.PushReceive_Reply, error) { - return s.pushHandler.PushReceive(ctx, request.Payload) +func (s *service) OutOfStoreReceive(ctx context.Context, request *protocoltypes.OutOfStoreReceive_Request) (*protocoltypes.OutOfStoreReceive_Reply, error) { + outOfStoreMessage, group, clearPayload, alreadyDecrypted, err := s.secretStore.OpenOutOfStoreMessage(ctx, request.Payload) + if err != nil { + return nil, errcode.ErrCryptoDecrypt.Wrap(err) + } + + return &protocoltypes.OutOfStoreReceive_Reply{ + Message: outOfStoreMessage, + Cleartext: clearPayload, + GroupPublicKey: group.PublicKey, + AlreadyReceived: alreadyDecrypted, + }, nil } -func (s *service) PushSend(ctx context.Context, request *protocoltypes.PushSend_Request) (*protocoltypes.PushSend_Reply, error) { +func (s *service) OutOfStoreSeal(ctx context.Context, request *protocoltypes.OutOfStoreSeal_Request) (*protocoltypes.OutOfStoreSeal_Reply, error) { gc, err := s.GetContextGroupForID(request.GroupPublicKey) if err != nil { return nil, err @@ -115,193 +89,14 @@ func (s *service) PushSend(ctx context.Context, request *protocoltypes.PushSend_ return nil, errcode.ErrInternal.Wrap(err) } - pushTargets, err := s.getPushTargetsByServer(gc, request.GroupMembers) - if err != nil { - return nil, errcode.ErrInternal.Wrap(err) - } - - if len(pushTargets) == 0 { - s.logger.Info("PushSend - pushing - no targets", logutil.PrivateString("cid", c.String())) - return &protocoltypes.PushSend_Reply{}, nil - } - - wg := sync.WaitGroup{} - wg.Add(len(pushTargets)) - - for serverAddr, pushTokens := range pushTargets { - // @FIXME(gfanton): find a better way to get service token - go func(serverAddr string, pushTokens []*pushtypes.PushServiceOpaqueReceiver) { - s.logger.Info("PushSend - pushing", logutil.PrivateString("cid", c.String()), logutil.PrivateString("server", serverAddr)) - defer wg.Done() - - if len(pushTokens) == 0 { - s.logger.Info("no push receivers", logutil.PrivateString("push-server", serverAddr)) - return - } - - client, err := s.getPushClient(serverAddr) - if err != nil { - s.logger.Error("error while dialing push server", logutil.PrivateString("push-server", serverAddr), zap.Error(err)) - return - } - - _, err = client.Send(ctx, &pushtypes.PushServiceSend_Request{ - Envelope: sealedMessageEnvelope, - Priority: pushtypes.PushServicePriority_PushPriorityNormal, - Receivers: pushTokens, - }, grpc.WaitForReady(true)) - if err != nil { - s.logger.Error("error while dialing push server", logutil.PrivateString("push-server", serverAddr), zap.Error(err)) - return - } - - s.logger.Debug("send push notification successfully", logutil.PrivateString("cid", c.String()), logutil.PrivateString("endpoint", serverAddr)) - }(serverAddr, pushTokens) - } - - wg.Wait() - - return &protocoltypes.PushSend_Reply{}, nil -} - -func (s *service) getPushTargetsByServer(gc *GroupContext, targetGroupMembers []*protocoltypes.MemberWithDevices) (map[string][]*pushtypes.PushServiceOpaqueReceiver, error) { - pushTargets := []*protocoltypes.PushMemberTokenUpdate(nil) - serverTokens := map[string][]*pushtypes.PushServiceOpaqueReceiver{} - targetDevices := []crypto.PubKey(nil) - - if len(targetGroupMembers) == 0 { - targetDevices = gc.metadataStore.ListOtherMembersDevices() - } else { - for _, memberAndDevices := range targetGroupMembers { - pk, err := crypto.UnmarshalEd25519PublicKey(memberAndDevices.MemberPK) - if err != nil { - return nil, errcode.ErrDeserialization.Wrap(err) - } - - if len(memberAndDevices.DevicePKs) == 0 { - // If no devices provided push all devices - devices, err := gc.metadataStore.GetDevicesForMember(pk) - if err != nil { - return nil, errcode.ErrInternal.Wrap(err) - } - - targetDevices = append(targetDevices, devices...) - continue - } - - for _, pkB := range memberAndDevices.DevicePKs { - devPK, err := crypto.UnmarshalEd25519PublicKey(pkB) - if err != nil { - return nil, errcode.ErrDeserialization.Wrap(err) - } - - member, err := gc.metadataStore.GetMemberByDevice(devPK) - if err != nil { - s.logger.Warn("error while retrieving member for device", zap.Error(err)) - continue - } - - if !member.Equals(devPK) { - s.logger.Warn("device does not belong to member") - continue - } - - targetDevices = append(targetDevices, devPK) - } - } - } - - for _, d := range targetDevices { - pushToken, err := gc.metadataStore.GetPushTokenForDevice(d) - if err != nil { - s.logger.Info("unable to get push token for device") - continue - } - - pushTargets = append(pushTargets, pushToken) - } - - for _, pushTarget := range pushTargets { - serverTokens[pushTarget.Server.ServiceAddr] = append(serverTokens[pushTarget.Server.ServiceAddr], &pushtypes.PushServiceOpaqueReceiver{OpaqueToken: pushTarget.Token}) - } - - return serverTokens, nil -} - -func (s *service) PushShareToken(ctx context.Context, request *protocoltypes.PushShareToken_Request) (*protocoltypes.PushShareToken_Reply, error) { - gc, err := s.GetContextGroupForID(request.GroupPK) - if err != nil { - return nil, errcode.ErrInvalidInput.Wrap(err) - } - - token, err := PushSealTokenForServer(request.Receiver, request.Server) + sealedMessageEnvelopeBytes, err := sealedMessageEnvelope.Marshal() if err != nil { - return nil, errcode.ErrInternal.Wrap(err) - } - - if _, err := gc.metadataStore.SendPushToken(ctx, token); err != nil { - return nil, err - } - s.logger.Debug("send push token done") - - return &protocoltypes.PushShareToken_Reply{}, nil -} - -func (s *service) PushSetDeviceToken(ctx context.Context, request *protocoltypes.PushSetDeviceToken_Request) (*protocoltypes.PushSetDeviceToken_Reply, error) { - s.lock.Lock() - defer s.lock.Unlock() - - if request.Receiver == nil || request.Receiver.TokenType == pushtypes.PushServiceTokenType_PushTokenUndefined { - return nil, errcode.ErrInvalidInput.Wrap(fmt.Errorf("invalid push token provided")) - } - - request.Receiver.RecipientPublicKey = s.pushHandler.PushPK()[:] - - if s.accountGroup == nil { - return nil, errcode.ErrGroupActivate.Wrap(fmt.Errorf("accountGroup is deactivated")) - } - - if currentReceiver := s.accountGroup.metadataStore.getCurrentDevicePushToken(); currentReceiver != nil && bytes.Equal(currentReceiver.Token, request.Receiver.Token) { - s.logger.Warn("push device token already set", logutil.PrivateString("b64 token", base64.StdEncoding.EncodeToString(request.Receiver.Token))) - return &protocoltypes.PushSetDeviceToken_Reply{}, nil - } - - if _, err := s.accountGroup.metadataStore.RegisterDevicePushToken(ctx, request.Receiver); err != nil { - return nil, errcode.ErrInternal.Wrap(err) - } - - s.logger.Debug("push token device set", zap.Int("token len", len(request.Receiver.Token))) - - return &protocoltypes.PushSetDeviceToken_Reply{}, nil -} - -func (s *service) PushSetServer(ctx context.Context, request *protocoltypes.PushSetServer_Request) (*protocoltypes.PushSetServer_Reply, error) { - s.lock.Lock() - defer s.lock.Unlock() - - if request.Server == nil { - return nil, errcode.ErrInvalidInput.Wrap(fmt.Errorf("no push server provided")) - } - - if s.accountGroup == nil { - return nil, errcode.ErrGroupActivate.Wrap(fmt.Errorf("accountGroup is deactivated")) - } - - if currentServer := s.accountGroup.metadataStore.getCurrentDevicePushServer(); currentServer != nil && - bytes.Equal(currentServer.ServerKey, request.Server.ServerKey) && - currentServer.ServiceAddr == request.Server.ServiceAddr { - return &protocoltypes.PushSetServer_Reply{}, nil - } - - if _, err := s.accountGroup.metadataStore.RegisterDevicePushServer(ctx, request.Server); err != nil { - return nil, errcode.ErrInternal.Wrap(err) - } - - if err := s.pushHandler.UpdatePushServer(ctx, request.Server); err != nil { - return nil, errcode.ErrInternal.Wrap(err) + return nil, errcode.ErrSerialization.Wrap(err) } - return &protocoltypes.PushSetServer_Reply{}, nil + return &protocoltypes.OutOfStoreSeal_Reply{ + Encrypted: sealedMessageEnvelopeBytes, + }, nil } func (s *service) GetCurrentDevicePushConfig() (*protocoltypes.PushServiceReceiver, *protocoltypes.PushServer) { diff --git a/api_push_test.go b/api_push_test.go deleted file mode 100644 index 4a09f32e..00000000 --- a/api_push_test.go +++ /dev/null @@ -1,246 +0,0 @@ -package weshnet_test - -import ( - "context" - crand "crypto/rand" - "testing" - - "github.com/ipfs/go-cid" - "github.com/stretchr/testify/require" - "golang.org/x/crypto/nacl/box" - - weshnet "berty.tech/weshnet" - "berty.tech/weshnet/pkg/bertypush" - "berty.tech/weshnet/pkg/cryptoutil" - "berty.tech/weshnet/pkg/protocoltypes" - "berty.tech/weshnet/pkg/pushtypes" -) - -func Test_sealPushMessage_decryptOutOfStoreMessageEnv(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - _, devicePushSK, err := box.GenerateKey(crand.Reader) - require.NoError(t, err) - - tp, cancel := weshnet.NewTestingProtocol(ctx, t, &weshnet.TestingOpts{PushSK: devicePushSK}, nil) - defer cancel() - - g, _, err := weshnet.NewGroupMultiMember() - require.NoError(t, err) - - s := tp.Service - - gPK, err := g.GetPubKey() - require.NoError(t, err) - - _, err = s.MultiMemberGroupJoin(ctx, &protocoltypes.MultiMemberGroupJoin_Request{Group: g}) - require.NoError(t, err) - - gPKRaw, err := gPK.Raw() - require.NoError(t, err) - - _, err = s.ActivateGroup(ctx, &protocoltypes.ActivateGroup_Request{GroupPK: gPKRaw}) - require.NoError(t, err) - - gc, err := s.(weshnet.ServiceMethods).GetContextGroupForID(g.PublicKey) - require.NoError(t, err) - - otherMD, otherDS := weshnet.CreateVirtualOtherPeerSecretsShareSecret(t, ctx, []*weshnet.MetadataStore{gc.MetadataStore()}) - - testPayload := []byte("test payload") - - envBytes, err := cryptoutil.SealEnvelope(testPayload, otherDS, otherMD.PrivateDevice(), g) - require.NoError(t, err) - - env, headers, err := cryptoutil.OpenEnvelopeHeaders(envBytes, g) - require.NoError(t, err) - - oosMsgEnv, err := weshnet.SealOutOfStoreMessageEnvelope(cid.Undef, env, headers, g) - require.NoError(t, err) - - openedOOSMessage, err := bertypush.DecryptOutOfStoreMessageEnv(ctx, tp.GroupDatastore, oosMsgEnv, gPK) - require.NoError(t, err) - - require.Equal(t, headers.Counter, openedOOSMessage.Counter) - require.Equal(t, headers.DevicePK, openedOOSMessage.DevicePK) - require.Equal(t, headers.Sig, openedOOSMessage.Sig) - require.Equal(t, env.Message, openedOOSMessage.EncryptedPayload) -} - -func TestService_PushShareToken(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - devicePushPK, devicePushSK, err := box.GenerateKey(crand.Reader) - require.NoError(t, err) - - serverPushPK, _, err := box.GenerateKey(crand.Reader) - require.NoError(t, err) - - tokenTestData := []byte("token_test_data_1") - const nameTestPackage = "test.app" - const serverAddr1 = "server1.test" - - tp, cancel := weshnet.NewTestingProtocol(ctx, t, &weshnet.TestingOpts{PushSK: devicePushSK}, nil) - defer cancel() - - s := tp.Service - - _, err = s.PushSetServer(ctx, &protocoltypes.PushSetServer_Request{Server: &protocoltypes.PushServer{ - ServerKey: serverPushPK[:], - ServiceAddr: serverAddr1, - }}) - require.NoError(t, err) - - _, err = s.PushSetDeviceToken(ctx, &protocoltypes.PushSetDeviceToken_Request{ - Receiver: &protocoltypes.PushServiceReceiver{ - TokenType: pushtypes.PushServiceTokenType_PushTokenApplePushNotificationService, - BundleID: nameTestPackage, - Token: tokenTestData, - RecipientPublicKey: devicePushPK[:], - }, - }) - require.NoError(t, err) - - g, gSK, err := weshnet.NewGroupMultiMember() - require.NoError(t, err) - - _, err = s.MultiMemberGroupJoin(ctx, &protocoltypes.MultiMemberGroupJoin_Request{Group: g}) - require.NoError(t, err) - - gPK, _ := gSK.GetPublic().Raw() - require.NoError(t, err) - - _, err = s.ActivateGroup(ctx, &protocoltypes.ActivateGroup_Request{GroupPK: gPK}) - require.NoError(t, err) - - gc, err := s.(weshnet.ServiceMethods).GetContextGroupForID(g.PublicKey) - require.NoError(t, err) - - pushToken, err := gc.MetadataStore().GetPushTokenForDevice(gc.DevicePubKey()) - require.Error(t, err) - require.Nil(t, pushToken) - - _, err = s.PushShareToken(ctx, &protocoltypes.PushShareToken_Request{ - GroupPK: g.PublicKey, - Server: &protocoltypes.PushServer{ - ServerKey: serverPushPK[:], - ServiceAddr: serverAddr1, - }, - Receiver: &protocoltypes.PushServiceReceiver{ - TokenType: pushtypes.PushServiceTokenType_PushTokenApplePushNotificationService, - BundleID: nameTestPackage, - Token: tokenTestData, - RecipientPublicKey: devicePushPK[:], - }, - }) - require.NoError(t, err) - - pushToken, err = gc.MetadataStore().GetPushTokenForDevice(gc.DevicePubKey()) - require.NoError(t, err) - require.NotNil(t, pushToken) - require.Equal(t, serverAddr1, pushToken.Server.ServiceAddr) - require.Equal(t, serverPushPK[:], pushToken.Server.ServerKey) -} - -func TestService_PushSetDeviceToken(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - devicePushPK, devicePushSK, err := box.GenerateKey(crand.Reader) - require.NoError(t, err) - - tokenTestData1 := []byte("token_test_data_1") - tokenTestData2 := []byte("token_test_data_2") - const nameTestPackage = "test.app" - - tp, cancel := weshnet.NewTestingProtocol(ctx, t, &weshnet.TestingOpts{PushSK: devicePushSK}, nil) - defer cancel() - - s := tp.Service - - currentPush, _ := s.(weshnet.ServiceMethods).GetCurrentDevicePushConfig() - require.Nil(t, currentPush) - - _, err = s.PushSetDeviceToken(ctx, &protocoltypes.PushSetDeviceToken_Request{ - Receiver: &protocoltypes.PushServiceReceiver{ - TokenType: pushtypes.PushServiceTokenType_PushTokenMQTT, - BundleID: nameTestPackage, - Token: tokenTestData1, - }, - }) - require.NoError(t, err) - - currentPush, _ = s.(weshnet.ServiceMethods).GetCurrentDevicePushConfig() - require.NotNil(t, currentPush) - require.Equal(t, tokenTestData1, currentPush.Token) - require.Equal(t, nameTestPackage, currentPush.BundleID) - require.Equal(t, pushtypes.PushServiceTokenType_PushTokenMQTT, currentPush.TokenType) - require.Equal(t, devicePushPK[:], currentPush.RecipientPublicKey) - - _, err = s.PushSetDeviceToken(ctx, &protocoltypes.PushSetDeviceToken_Request{ - Receiver: &protocoltypes.PushServiceReceiver{ - TokenType: pushtypes.PushServiceTokenType_PushTokenApplePushNotificationService, - BundleID: nameTestPackage, - Token: tokenTestData2, - }, - }) - require.NoError(t, err) - - currentPush, _ = s.(weshnet.ServiceMethods).GetCurrentDevicePushConfig() - require.NotNil(t, currentPush) - require.Equal(t, tokenTestData2, currentPush.Token) - require.Equal(t, nameTestPackage, currentPush.BundleID) - require.Equal(t, pushtypes.PushServiceTokenType_PushTokenApplePushNotificationService, currentPush.TokenType) - require.Equal(t, devicePushPK[:], currentPush.RecipientPublicKey) -} - -func TestService_PushSetServer(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - const serverAddr1 = "server1.test" - const serverAddr2 = "server2.test" - - _, devicePushSK, err := box.GenerateKey(crand.Reader) - require.NoError(t, err) - - serverPushPK1, _, err := box.GenerateKey(crand.Reader) - require.NoError(t, err) - - serverPushPK2, _, err := box.GenerateKey(crand.Reader) - require.NoError(t, err) - - tp, cancel := weshnet.NewTestingProtocol(ctx, t, &weshnet.TestingOpts{PushSK: devicePushSK}, nil) - defer cancel() - - s := tp.Service - - _, currentPush := s.(weshnet.ServiceMethods).GetCurrentDevicePushConfig() - require.Nil(t, currentPush) - - _, err = s.PushSetServer(ctx, &protocoltypes.PushSetServer_Request{Server: &protocoltypes.PushServer{ - ServerKey: serverPushPK1[:], - ServiceAddr: serverAddr1, - }}) - require.NoError(t, err) - - _, currentPush = s.(weshnet.ServiceMethods).GetCurrentDevicePushConfig() - require.NotNil(t, currentPush) - require.Equal(t, serverPushPK1[:], currentPush.ServerKey) - require.Equal(t, serverAddr1, currentPush.ServiceAddr) - - _, err = s.PushSetServer(ctx, &protocoltypes.PushSetServer_Request{Server: &protocoltypes.PushServer{ - ServerKey: serverPushPK2[:], - ServiceAddr: serverAddr2, - }}) - require.NoError(t, err) - - _, currentPush = s.(weshnet.ServiceMethods).GetCurrentDevicePushConfig() - require.NotNil(t, currentPush) - require.Equal(t, serverPushPK2[:], currentPush.ServerKey) - require.Equal(t, serverAddr2, currentPush.ServiceAddr) - - // FIXME: Should we add a way to clear the push server used with the current device? -} diff --git a/api_replication.go b/api_replication.go index 45776113..b2cf0741 100644 --- a/api_replication.go +++ b/api_replication.go @@ -12,7 +12,6 @@ import ( "google.golang.org/grpc/credentials/insecure" "berty.tech/weshnet/pkg/authtypes" - "berty.tech/weshnet/pkg/cryptoutil" "berty.tech/weshnet/pkg/errcode" "berty.tech/weshnet/pkg/grpcutil" "berty.tech/weshnet/pkg/logutil" @@ -32,7 +31,7 @@ func FilterGroupForReplication(m *protocoltypes.Group) (*protocoltypes.Group, er return nil, errcode.ErrSerialization.Wrap(err) } - linkKey, err := cryptoutil.GetLinkKeyArray(m) + linkKey, err := m.GetLinkKeyArray() if err != nil { return nil, errcode.TODO.Wrap(err) } diff --git a/api_services_auth.go b/api_services_auth.go index 03fb4e28..2888f9a5 100644 --- a/api_services_auth.go +++ b/api_services_auth.go @@ -163,7 +163,7 @@ func (s *service) AuthServiceCompleteFlow(ctx context.Context, request *protocol } // @FIXME(gfanton): should be handle on the client (js) side - registeredPushServer := 0 + // registeredPushServer := 0 for _, service := range services { if service.ServiceType != authtypes.ServicePushID { continue @@ -185,25 +185,25 @@ func (s *service) AuthServiceCompleteFlow(ctx context.Context, request *protocol continue } - _, err = s.PushSetServer(ctx, &protocoltypes.PushSetServer_Request{ - Server: &protocoltypes.PushServer{ - ServerKey: repl.PublicKey, - ServiceAddr: service.ServiceEndpoint, - }, - }) - - if err != nil { - s.logger.Warn("unable to set push server", zap.Error(err)) - continue - } - - registeredPushServer++ - s.logger.Debug("push server registered", logutil.PrivateString("endpoint", service.GetServiceEndpoint())) + // _, err = s.PushSetServer(ctx, &protocoltypes.PushSetServer_Request{ + // Server: &protocoltypes.PushServer{ + // ServerKey: repl.PublicKey, + // ServiceAddr: service.ServiceEndpoint, + // }, + // }) + + // if err != nil { + // s.logger.Warn("unable to set push server", zap.Error(err)) + // continue + // } + // + // registeredPushServer++ + // s.logger.Debug("push server registered", logutil.PrivateString("endpoint", service.GetServiceEndpoint())) } - if registeredPushServer == 0 { - s.logger.Warn("no push server found/registered") - } + // if registeredPushServer == 0 { + // s.logger.Warn("no push server found/registered") + // } return &protocoltypes.AuthServiceCompleteFlow_Reply{ TokenID: svcToken.TokenID(), diff --git a/api_verified_credentials.go b/api_verified_credentials.go index e7b6164c..56f8110d 100644 --- a/api_verified_credentials.go +++ b/api_verified_credentials.go @@ -3,31 +3,16 @@ package weshnet import ( "bytes" "context" - stdcrypto "crypto" "fmt" - "io" "strings" "time" - libp2p_ci "github.com/libp2p/go-libp2p/core/crypto" - "berty.tech/weshnet/pkg/bertyvcissuer" + "berty.tech/weshnet/pkg/cryptoutil" "berty.tech/weshnet/pkg/errcode" "berty.tech/weshnet/pkg/protocoltypes" ) -type signerWrapper struct { - libp2p_ci.PrivKey -} - -func (s *signerWrapper) Public() stdcrypto.PublicKey { - return s.PrivKey.GetPublic() -} - -func (s *signerWrapper) Sign(_ io.Reader, digest []byte, _ stdcrypto.SignerOpts) (signature []byte, err error) { - return s.PrivKey.Sign(digest) -} - func (s *service) CredentialVerificationServiceInitFlow(ctx context.Context, request *protocoltypes.CredentialVerificationServiceInitFlow_Request) (*protocoltypes.CredentialVerificationServiceInitFlow_Reply, error) { s.lock.Lock() s.vcClient = bertyvcissuer.NewClient(request.ServiceURL) @@ -38,12 +23,8 @@ func (s *service) CredentialVerificationServiceInitFlow(ctx context.Context, req defer cancel() // TODO: allow selection of alt-scoped keys - sk, err := s.deviceKeystore.AccountPrivKey() - if err != nil { - return nil, errcode.ErrInvalidInput - } - - pkRaw, err := sk.GetPublic().Raw() + // TODO: avoid exporting account keys + pkRaw, err := s.accountGroupCtx.ownMemberDevice.Member().Raw() if err != nil { return nil, errcode.ErrInvalidInput } @@ -52,7 +33,7 @@ func (s *service) CredentialVerificationServiceInitFlow(ctx context.Context, req return nil, errcode.ErrInvalidInput } - url, err := client.Init(ctx, request.Link, &signerWrapper{sk}) + url, err := client.Init(ctx, request.Link, cryptoutil.NewFuncSigner(s.accountGroupCtx.ownMemberDevice.Member(), s.accountGroupCtx.ownMemberDevice.MemberSign)) if err != nil { return nil, errcode.ErrInternal.Wrap(err) } @@ -77,7 +58,7 @@ func (s *service) CredentialVerificationServiceCompleteFlow(ctx context.Context, return nil, errcode.ErrInternal.Wrap(err) } - _, err = s.accountGroup.metadataStore.SendAccountVerifiedCredentialAdded(ctx, &protocoltypes.AccountVerifiedCredentialRegistered{ + _, err = s.accountGroupCtx.metadataStore.SendAccountVerifiedCredentialAdded(ctx, &protocoltypes.AccountVerifiedCredentialRegistered{ VerifiedCredential: credentials, RegistrationDate: parsedCredential.Issued.UnixNano(), ExpirationDate: parsedCredential.Expired.UnixNano(), @@ -95,7 +76,7 @@ func (s *service) CredentialVerificationServiceCompleteFlow(ctx context.Context, func (s *service) VerifiedCredentialsList(request *protocoltypes.VerifiedCredentialsList_Request, server protocoltypes.ProtocolService_VerifiedCredentialsListServer) error { now := time.Now().UnixNano() - credentials := s.accountGroup.metadataStore.ListVerifiedCredentials() + credentials := s.accountGroupCtx.metadataStore.ListVerifiedCredentials() for _, credential := range credentials { if request.FilterIdentifier != "" && credential.Identifier != request.FilterIdentifier { diff --git a/blackbox_test.go b/blackbox_test.go index 19c6d4ad..9857572b 100644 --- a/blackbox_test.go +++ b/blackbox_test.go @@ -7,12 +7,12 @@ import ( "os" "testing" - keystore "github.com/ipfs/go-ipfs-keystore" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "berty.tech/weshnet" - "berty.tech/weshnet/pkg/cryptoutil" "berty.tech/weshnet/pkg/protocoltypes" + "berty.tech/weshnet/pkg/secretstore" "berty.tech/weshnet/pkg/testutil" ) @@ -23,9 +23,12 @@ func TestTestingClient_impl(t *testing.T) { logger, cleanup := testutil.Logger(t) defer cleanup() + secretStore, err := secretstore.NewInMemSecretStore(nil) + require.NoError(t, err) + client, cleanup := weshnet.TestingService(ctx, t, weshnet.Opts{ - Logger: logger, - DeviceKeystore: cryptoutil.NewDeviceKeystore(keystore.NewMemKeystore(), nil), + Logger: logger, + SecretStore: secretStore, }) defer cleanup() diff --git a/consts.go b/consts.go index c0742f23..386e48e2 100644 --- a/consts.go +++ b/consts.go @@ -5,7 +5,6 @@ import ( ) const ( - NamespaceDeviceKeystore = "device_keystore" NamespaceOrbitDBDatastore = "orbitdb_datastore" NamespaceOrbitDBDirectory = "orbitdb" NamespaceIPFSDatastore = "ipfs_datastore" diff --git a/contact_request_manager.go b/contact_request_manager.go index 47bc9637..4871e86b 100644 --- a/contact_request_manager.go +++ b/contact_request_manager.go @@ -42,7 +42,7 @@ type contactRequestsManager struct { enabled bool ownRendezvousSeed []byte - accSK crypto.PrivKey + accountPrivateKey crypto.PrivKey ipfs ipfsutil.ExtendedCoreAPI swiper *Swiper @@ -50,7 +50,7 @@ type contactRequestsManager struct { } func newContactRequestsManager(s *Swiper, store *MetadataStore, ipfs ipfsutil.ExtendedCoreAPI, logger *zap.Logger) (*contactRequestsManager, error) { - sk, err := store.devKS.AccountPrivKey() + accountPrivateKey, err := store.secretStore.GetAccountPrivateKey() if err != nil { return nil, err } @@ -58,14 +58,14 @@ func newContactRequestsManager(s *Swiper, store *MetadataStore, ipfs ipfsutil.Ex ctx, cancel := context.WithCancel(context.Background()) cm := &contactRequestsManager{ - lookupProcess: make(map[string]context.CancelFunc), - metadataStore: store, - ipfs: ipfs, - logger: logger.Named("req-mngr"), - accSK: sk, - ctx: ctx, - cancel: cancel, - swiper: s, + lookupProcess: make(map[string]context.CancelFunc), + metadataStore: store, + ipfs: ipfs, + logger: logger.Named("req-mngr"), + accountPrivateKey: accountPrivateKey, + ctx: ctx, + cancel: cancel, + swiper: s, } go cm.metadataWatcher(ctx) @@ -199,7 +199,7 @@ func (c *contactRequestsManager) enableContactRequest(ctx context.Context) error return nil } - pkBytes, err := c.accSK.GetPublic().Raw() + pkBytes, err := c.accountPrivateKey.GetPublic().Raw() if err != nil { return fmt.Errorf("unable to get raw pk: %w", err) } @@ -236,7 +236,7 @@ func (c *contactRequestsManager) metadataRequestReset(ctx context.Context, evt * return errcode.ErrDeserialization.Wrap(err) } - accPK, err := c.accSK.GetPublic().Raw() + accPK, err := c.accountPrivateKey.GetPublic().Raw() if err != nil { return fmt.Errorf("unable to get raw pk: %w", err) } @@ -452,7 +452,7 @@ func (c *contactRequestsManager) SendContactRequest(ctx context.Context, to *pro c.logger.Debug("performing handshake") tyber.LogStep(ctx, c.logger, "performing handshake") - if err := handshake.RequestUsingReaderWriter(ctx, c.logger, reader, writer, c.accSK, otherPK); err != nil { + if err := handshake.RequestUsingReaderWriter(ctx, c.logger, reader, writer, c.accountPrivateKey, otherPK); err != nil { return fmt.Errorf("an error occurred during handshake: %w", err) } @@ -463,7 +463,7 @@ func (c *contactRequestsManager) SendContactRequest(ctx context.Context, to *pro } tyber.LogStep(ctx, c.logger, "mark contact request has sent") - // mark this contact request has send + // mark this contact request as sent if _, err := c.metadataStore.ContactRequestOutgoingSent(ctx, otherPK); err != nil { return fmt.Errorf("an error occurred while marking contact request as sent: %w", err) } @@ -477,7 +477,7 @@ func (c *contactRequestsManager) handleIncomingRequest(ctx context.Context, stre tyber.LogStep(ctx, c.logger, "responding to handshake") - otherPK, err := handshake.ResponseUsingReaderWriter(ctx, c.logger, reader, writer, c.accSK) + otherPK, err := handshake.ResponseUsingReaderWriter(ctx, c.logger, reader, writer, c.accountPrivateKey) if err != nil { return fmt.Errorf("handshake failed: %w", err) } diff --git a/docs/apis/protocoltypes.md b/docs/apis/protocoltypes.md index 1702f364..48ba97ac 100644 --- a/docs/apis/protocoltypes.md +++ b/docs/apis/protocoltypes.md @@ -90,13 +90,13 @@ - [DebugListGroups](#weshnet-protocol-v1-DebugListGroups) - [DebugListGroups.Reply](#weshnet-protocol-v1-DebugListGroups-Reply) - [DebugListGroups.Request](#weshnet-protocol-v1-DebugListGroups-Request) - - [DeviceSecret](#weshnet-protocol-v1-DeviceSecret) + - [DeviceChainKey](#weshnet-protocol-v1-DeviceChainKey) - [EncryptedMessage](#weshnet-protocol-v1-EncryptedMessage) - [EventContext](#weshnet-protocol-v1-EventContext) - [FirstLastCounters](#weshnet-protocol-v1-FirstLastCounters) - [Group](#weshnet-protocol-v1-Group) - [GroupAddAdditionalRendezvousSeed](#weshnet-protocol-v1-GroupAddAdditionalRendezvousSeed) - - [GroupAddDeviceSecret](#weshnet-protocol-v1-GroupAddDeviceSecret) + - [GroupAddDeviceChainKey](#weshnet-protocol-v1-GroupAddDeviceChainKey) - [GroupAddMemberDevice](#weshnet-protocol-v1-GroupAddMemberDevice) - [GroupDeviceStatus](#weshnet-protocol-v1-GroupDeviceStatus) - [GroupDeviceStatus.Reply](#weshnet-protocol-v1-GroupDeviceStatus-Reply) @@ -146,6 +146,12 @@ - [OrbitDBMessageHeads](#weshnet-protocol-v1-OrbitDBMessageHeads) - [OrbitDBMessageHeads.Box](#weshnet-protocol-v1-OrbitDBMessageHeads-Box) - [OutOfStoreMessage](#weshnet-protocol-v1-OutOfStoreMessage) + - [OutOfStoreReceive](#weshnet-protocol-v1-OutOfStoreReceive) + - [OutOfStoreReceive.Reply](#weshnet-protocol-v1-OutOfStoreReceive-Reply) + - [OutOfStoreReceive.Request](#weshnet-protocol-v1-OutOfStoreReceive-Request) + - [OutOfStoreSeal](#weshnet-protocol-v1-OutOfStoreSeal) + - [OutOfStoreSeal.Reply](#weshnet-protocol-v1-OutOfStoreSeal-Reply) + - [OutOfStoreSeal.Request](#weshnet-protocol-v1-OutOfStoreSeal-Request) - [PeerList](#weshnet-protocol-v1-PeerList) - [PeerList.Peer](#weshnet-protocol-v1-PeerList-Peer) - [PeerList.Reply](#weshnet-protocol-v1-PeerList-Reply) @@ -157,23 +163,8 @@ - [PushDeviceServerRegistered](#weshnet-protocol-v1-PushDeviceServerRegistered) - [PushDeviceTokenRegistered](#weshnet-protocol-v1-PushDeviceTokenRegistered) - [PushMemberTokenUpdate](#weshnet-protocol-v1-PushMemberTokenUpdate) - - [PushReceive](#weshnet-protocol-v1-PushReceive) - - [PushReceive.Reply](#weshnet-protocol-v1-PushReceive-Reply) - - [PushReceive.Request](#weshnet-protocol-v1-PushReceive-Request) - - [PushSend](#weshnet-protocol-v1-PushSend) - - [PushSend.Reply](#weshnet-protocol-v1-PushSend-Reply) - - [PushSend.Request](#weshnet-protocol-v1-PushSend-Request) - [PushServer](#weshnet-protocol-v1-PushServer) - [PushServiceReceiver](#weshnet-protocol-v1-PushServiceReceiver) - - [PushSetDeviceToken](#weshnet-protocol-v1-PushSetDeviceToken) - - [PushSetDeviceToken.Reply](#weshnet-protocol-v1-PushSetDeviceToken-Reply) - - [PushSetDeviceToken.Request](#weshnet-protocol-v1-PushSetDeviceToken-Request) - - [PushSetServer](#weshnet-protocol-v1-PushSetServer) - - [PushSetServer.Reply](#weshnet-protocol-v1-PushSetServer-Reply) - - [PushSetServer.Request](#weshnet-protocol-v1-PushSetServer-Request) - - [PushShareToken](#weshnet-protocol-v1-PushShareToken) - - [PushShareToken.Reply](#weshnet-protocol-v1-PushShareToken-Reply) - - [PushShareToken.Request](#weshnet-protocol-v1-PushShareToken-Request) - [RefreshContactRequest](#weshnet-protocol-v1-RefreshContactRequest) - [RefreshContactRequest.Peer](#weshnet-protocol-v1-RefreshContactRequest-Peer) - [RefreshContactRequest.Reply](#weshnet-protocol-v1-RefreshContactRequest-Reply) @@ -253,7 +244,7 @@ AccountContactBlocked indicates that a contact is blocked ### AccountContactRequestAccepted This event should be followed by an AccountGroupJoined event -This event should be followed by GroupAddMemberDevice and GroupAddDeviceSecret events within the AccountGroup +This event should be followed by GroupAddMemberDevice and GroupAddDeviceChainKey events within the AccountGroup AccountContactRequestAccepted indicates that a contact request has been accepted | Field | Type | Label | Description | @@ -295,7 +286,7 @@ AccountContactRequestDisabled indicates that the account should be advertised on ### AccountContactRequestEnqueued This event should be followed by an AccountGroupJoined event This event should be followed by a GroupAddMemberDevice event within the AccountGroup -This event should be followed by a GroupAddDeviceSecret event within the AccountGroup +This event should be followed by a GroupAddDeviceChainKey event within the AccountGroup AccountContactRequestEnqueued indicates that the account will attempt to send a contact request when a matching peer is discovered | Field | Type | Label | Description | @@ -840,10 +831,10 @@ ContactAddAliasKey is an event type where ones shares their alias public key ### DebugListGroups.Request - + -### DeviceSecret -DeviceSecret is encrypted for a specific member of the group +### DeviceChainKey +DeviceChainKey is a chain key, which will be encrypted for a specific member of the group | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | @@ -890,7 +881,7 @@ Group define a group and is enough to invite someone to it | public_key | [bytes](#bytes) | | public_key is the identifier of the group, it signs the group secret and the initial member of a multi-member group | | secret | [bytes](#bytes) | | secret is the symmetric secret of the group, which is used to encrypt the metadata | | secret_sig | [bytes](#bytes) | | secret_sig is the signature of the secret used to ensure the validity of the group | -| group_type | [GroupType](#weshnet-protocol-v1-GroupType) | | group_type specifies the type of the group, used to determine how device secrets are generated | +| group_type | [GroupType](#weshnet-protocol-v1-GroupType) | | group_type specifies the type of the group, used to determine how device chain key is generated | | sign_pub | [bytes](#bytes) | | sign_pub is the signature public key used to verify entries, not required when secret and secret_sig are provided | | link_key | [bytes](#bytes) | | link_key is the secret key used to exchange group updates and links to attachments, useful for replication services | | link_key_sig | [bytes](#bytes) | | link_key_sig is the signature of the link_key using the group private key | @@ -905,10 +896,10 @@ GroupAddAdditionalRendezvousSeed indicates that an additional rendezvous point s | device_pk | [bytes](#bytes) | | device_pk is the device sending the event, signs the message, must be the device of an admin of the group | | seed | [bytes](#bytes) | | seed is the additional rendezvous point seed which should be used | - + -### GroupAddDeviceSecret -GroupAddDeviceSecret is an event which indicates to a group member a device secret +### GroupAddDeviceChainKey +GroupAddDeviceChainKey is an event which indicates to a group member a device chain key | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | @@ -920,7 +911,7 @@ GroupAddDeviceSecret is an event which indicates to a group member a device secr ### GroupAddMemberDevice GroupAddMemberDevice is an event which indicates to a group a new device (and eventually a new member) is joining it -When added on AccountGroup, this event should be followed by appropriate GroupAddMemberDevice and GroupAddDeviceSecret events +When added on AccountGroup, this event should be followed by appropriate GroupAddMemberDevice and GroupAddDeviceChainKey events | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | @@ -1316,6 +1307,50 @@ OrbitDBMessageHeads is the payload sent on orbitdb to share peer's heads | encrypted_payload | [bytes](#bytes) | | | | nonce | [bytes](#bytes) | | | + + +### OutOfStoreReceive + + + +### OutOfStoreReceive.Reply + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| message | [OutOfStoreMessage](#weshnet-protocol-v1-OutOfStoreMessage) | | | +| cleartext | [bytes](#bytes) | | | +| group_public_key | [bytes](#bytes) | | | +| already_received | [bool](#bool) | | | + + + +### OutOfStoreReceive.Request + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| payload | [bytes](#bytes) | | | + + + +### OutOfStoreSeal + + + +### OutOfStoreSeal.Reply + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| encrypted | [bytes](#bytes) | | | + + + +### OutOfStoreSeal.Request + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| cid | [bytes](#bytes) | | | +| group_public_key | [bytes](#bytes) | | | + ### PeerList @@ -1412,51 +1447,6 @@ Progress define a generic object that can be used to display a progress bar for | token | [bytes](#bytes) | | | | device_pk | [bytes](#bytes) | | device_pk is the public key of the device sending the message | - - -### PushReceive - - - -### PushReceive.Reply - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| message | [OutOfStoreMessage](#weshnet-protocol-v1-OutOfStoreMessage) | | | -| cleartext | [bytes](#bytes) | | | -| group_public_key | [bytes](#bytes) | | | -| already_received | [bool](#bool) | | | - - - -### PushReceive.Request - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| payload | [bytes](#bytes) | | | - - - -### PushSend - - - -### PushSend.Reply - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| group_members | [MemberWithDevices](#weshnet-protocol-v1-MemberWithDevices) | repeated | | - - - -### PushSend.Request - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| cid | [bytes](#bytes) | | | -| group_public_key | [bytes](#bytes) | | | -| group_members | [MemberWithDevices](#weshnet-protocol-v1-MemberWithDevices) | repeated | | - ### PushServer @@ -1477,56 +1467,6 @@ Progress define a generic object that can be used to display a progress bar for | token | [bytes](#bytes) | | token is the device identifier used | | recipient_public_key | [bytes](#bytes) | | recipient_public_key is the public key which will be used to encrypt the payload | - - -### PushSetDeviceToken - - - -### PushSetDeviceToken.Reply - - - -### PushSetDeviceToken.Request - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| receiver | [PushServiceReceiver](#weshnet-protocol-v1-PushServiceReceiver) | | | - - - -### PushSetServer - - - -### PushSetServer.Reply - - - -### PushSetServer.Request - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| server | [PushServer](#weshnet-protocol-v1-PushServer) | | | - - - -### PushShareToken - - - -### PushShareToken.Reply - - - -### PushShareToken.Request - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| group_pk | [bytes](#bytes) | | | -| server | [PushServer](#weshnet-protocol-v1-PushServer) | | | -| receiver | [PushServiceReceiver](#weshnet-protocol-v1-PushServiceReceiver) | | | - ### RefreshContactRequest @@ -1835,7 +1775,7 @@ Progress define a generic object that can be used to display a progress bar for | ---- | ------ | ----------- | | EventTypeUndefined | 0 | EventTypeUndefined indicates that the value has not been set. Should not happen. | | EventTypeGroupMemberDeviceAdded | 1 | EventTypeGroupMemberDeviceAdded indicates the payload includes that a member has added their device to the group | -| EventTypeGroupDeviceSecretAdded | 2 | EventTypeGroupDeviceSecretAdded indicates the payload includes that a member has sent their device secret to another member | +| EventTypeGroupDeviceChainKeyAdded | 2 | EventTypeGroupDeviceChainKeyAdded indicates the payload includes that a member has sent their device chain key to another member | | EventTypeAccountGroupJoined | 101 | EventTypeAccountGroupJoined indicates the payload includes that the account has joined a group | | EventTypeAccountGroupLeft | 102 | EventTypeAccountGroupLeft indicates the payload includes that the account has left a group | | EventTypeAccountContactRequestDisabled | 103 | EventTypeAccountContactRequestDisabled indicates the payload includes that the account has disabled incoming contact requests | @@ -1969,11 +1909,8 @@ Each active Wesh protocol service is considered as a Wesh device and is associat | ServicesTokenList | [ServicesTokenList.Request](#weshnet-protocol-v1-ServicesTokenList-Request) | [ServicesTokenList.Reply](#weshnet-protocol-v1-ServicesTokenList-Reply) stream | ServicesTokenList Retrieves the list of services tokens | | ReplicationServiceRegisterGroup | [ReplicationServiceRegisterGroup.Request](#weshnet-protocol-v1-ReplicationServiceRegisterGroup-Request) | [ReplicationServiceRegisterGroup.Reply](#weshnet-protocol-v1-ReplicationServiceRegisterGroup-Reply) | ReplicationServiceRegisterGroup Asks a replication service to distribute a group contents | | PeerList | [PeerList.Request](#weshnet-protocol-v1-PeerList-Request) | [PeerList.Reply](#weshnet-protocol-v1-PeerList-Reply) | PeerList returns a list of P2P peers | -| PushReceive | [PushReceive.Request](#weshnet-protocol-v1-PushReceive-Request) | [PushReceive.Reply](#weshnet-protocol-v1-PushReceive-Reply) | PushReceive handles a push payload, decrypts it if possible | -| PushSend | [PushSend.Request](#weshnet-protocol-v1-PushSend-Request) | [PushSend.Reply](#weshnet-protocol-v1-PushSend-Reply) | PushSend sends a push payload to a specified list of group members | -| PushShareToken | [PushShareToken.Request](#weshnet-protocol-v1-PushShareToken-Request) | [PushShareToken.Reply](#weshnet-protocol-v1-PushShareToken-Reply) | PushShareToken sends push tokens of own devices to a group | -| PushSetDeviceToken | [PushSetDeviceToken.Request](#weshnet-protocol-v1-PushSetDeviceToken-Request) | [PushSetDeviceToken.Reply](#weshnet-protocol-v1-PushSetDeviceToken-Reply) | PushSetDeviceToken registers a push token for the current device | -| PushSetServer | [PushSetServer.Request](#weshnet-protocol-v1-PushSetServer-Request) | [PushSetServer.Reply](#weshnet-protocol-v1-PushSetServer-Reply) | PushSetServer registers a push server for the current device | +| OutOfStoreReceive | [OutOfStoreReceive.Request](#weshnet-protocol-v1-OutOfStoreReceive-Request) | [OutOfStoreReceive.Reply](#weshnet-protocol-v1-OutOfStoreReceive-Reply) | OutOfStoreReceive parses a payload received outside a synchronized store | +| OutOfStoreSeal | [OutOfStoreSeal.Request](#weshnet-protocol-v1-OutOfStoreSeal-Request) | [OutOfStoreSeal.Reply](#weshnet-protocol-v1-OutOfStoreSeal-Reply) | OutOfStoreSeal creates a payload of a message present in store to be sent outside a synchronized store | | RefreshContactRequest | [RefreshContactRequest.Request](#weshnet-protocol-v1-RefreshContactRequest-Request) | [RefreshContactRequest.Reply](#weshnet-protocol-v1-RefreshContactRequest-Reply) | RefreshContactRequest try to refresh the contact request for the given contact | diff --git a/docs/apis/protocoltypes.swagger.json b/docs/apis/protocoltypes.swagger.json index a515d2f2..d3593954 100644 --- a/docs/apis/protocoltypes.swagger.json +++ b/docs/apis/protocoltypes.swagger.json @@ -537,7 +537,7 @@ "enum": [ "EventTypeUndefined", "EventTypeGroupMemberDeviceAdded", - "EventTypeGroupDeviceSecretAdded", + "EventTypeGroupDeviceChainKeyAdded", "EventTypeAccountGroupJoined", "EventTypeAccountGroupLeft", "EventTypeAccountContactRequestDisabled", @@ -564,7 +564,7 @@ "EventTypeGroupMetadataPayloadSent" ], "default": "EventTypeUndefined", - "title": "- EventTypeUndefined: EventTypeUndefined indicates that the value has not been set. Should not happen.\n - EventTypeGroupMemberDeviceAdded: EventTypeGroupMemberDeviceAdded indicates the payload includes that a member has added their device to the group\n - EventTypeGroupDeviceSecretAdded: EventTypeGroupDeviceSecretAdded indicates the payload includes that a member has sent their device secret to another member\n - EventTypeAccountGroupJoined: EventTypeAccountGroupJoined indicates the payload includes that the account has joined a group\n - EventTypeAccountGroupLeft: EventTypeAccountGroupLeft indicates the payload includes that the account has left a group\n - EventTypeAccountContactRequestDisabled: EventTypeAccountContactRequestDisabled indicates the payload includes that the account has disabled incoming contact requests\n - EventTypeAccountContactRequestEnabled: EventTypeAccountContactRequestEnabled indicates the payload includes that the account has enabled incoming contact requests\n - EventTypeAccountContactRequestReferenceReset: EventTypeAccountContactRequestReferenceReset indicates the payload includes that the account has a new contact request rendezvous seed\n - EventTypeAccountContactRequestOutgoingEnqueued: EventTypeAccountContactRequestEnqueued indicates the payload includes that the account will attempt to send a new contact request\n - EventTypeAccountContactRequestOutgoingSent: EventTypeAccountContactRequestSent indicates the payload includes that the account has sent a contact request\n - EventTypeAccountContactRequestIncomingReceived: EventTypeAccountContactRequestReceived indicates the payload includes that the account has received a contact request\n - EventTypeAccountContactRequestIncomingDiscarded: EventTypeAccountContactRequestIncomingDiscarded indicates the payload includes that the account has ignored a contact request\n - EventTypeAccountContactRequestIncomingAccepted: EventTypeAccountContactRequestAccepted indicates the payload includes that the account has accepted a contact request\n - EventTypeAccountContactBlocked: EventTypeAccountContactBlocked indicates the payload includes that the account has blocked a contact\n - EventTypeAccountContactUnblocked: EventTypeAccountContactUnblocked indicates the payload includes that the account has unblocked a contact\n - EventTypeContactAliasKeyAdded: EventTypeContactAliasKeyAdded indicates the payload includes that the contact group has received an alias key\n - EventTypeMultiMemberGroupAliasResolverAdded: EventTypeMultiMemberGroupAliasResolverAdded indicates the payload includes that a member of the group sent their alias proof\n - EventTypeMultiMemberGroupInitialMemberAnnounced: EventTypeMultiMemberGroupInitialMemberAnnounced indicates the payload includes that a member has authenticated themselves as the group owner\n - EventTypeMultiMemberGroupAdminRoleGranted: EventTypeMultiMemberGroupAdminRoleGranted indicates the payload includes that an admin of the group granted another member as an admin\n - EventTypeAccountServiceTokenAdded: EventTypeAccountServiceTokenAdded indicates that a new service provider has been registered for this account\n - EventTypeAccountServiceTokenRemoved: EventTypeAccountServiceTokenRemoved indicates that a service provider is not available anymore\n - EventTypeGroupReplicating: EventTypeGroupReplicating indicates that the group has been registered for replication on a server\n - EventTypePushMemberTokenUpdate: EventTypePushMemberTokenUpdate\n - EventTypePushDeviceTokenRegistered: EventTypePushDeviceTokenRegistered\n - EventTypePushDeviceServerRegistered: EventTypePushDeviceServerRegistered\n - EventTypeAccountVerifiedCredentialRegistered: EventTypeAccountVerifiedCredentialRegistered\n - EventTypeGroupMetadataPayloadSent: EventTypeGroupMetadataPayloadSent indicates the payload includes an app specific event, unlike messages stored on the message store it is encrypted using a static key" + "title": "- EventTypeUndefined: EventTypeUndefined indicates that the value has not been set. Should not happen.\n - EventTypeGroupMemberDeviceAdded: EventTypeGroupMemberDeviceAdded indicates the payload includes that a member has added their device to the group\n - EventTypeGroupDeviceChainKeyAdded: EventTypeGroupDeviceChainKeyAdded indicates the payload includes that a member has sent their device chain key to another member\n - EventTypeAccountGroupJoined: EventTypeAccountGroupJoined indicates the payload includes that the account has joined a group\n - EventTypeAccountGroupLeft: EventTypeAccountGroupLeft indicates the payload includes that the account has left a group\n - EventTypeAccountContactRequestDisabled: EventTypeAccountContactRequestDisabled indicates the payload includes that the account has disabled incoming contact requests\n - EventTypeAccountContactRequestEnabled: EventTypeAccountContactRequestEnabled indicates the payload includes that the account has enabled incoming contact requests\n - EventTypeAccountContactRequestReferenceReset: EventTypeAccountContactRequestReferenceReset indicates the payload includes that the account has a new contact request rendezvous seed\n - EventTypeAccountContactRequestOutgoingEnqueued: EventTypeAccountContactRequestEnqueued indicates the payload includes that the account will attempt to send a new contact request\n - EventTypeAccountContactRequestOutgoingSent: EventTypeAccountContactRequestSent indicates the payload includes that the account has sent a contact request\n - EventTypeAccountContactRequestIncomingReceived: EventTypeAccountContactRequestReceived indicates the payload includes that the account has received a contact request\n - EventTypeAccountContactRequestIncomingDiscarded: EventTypeAccountContactRequestIncomingDiscarded indicates the payload includes that the account has ignored a contact request\n - EventTypeAccountContactRequestIncomingAccepted: EventTypeAccountContactRequestAccepted indicates the payload includes that the account has accepted a contact request\n - EventTypeAccountContactBlocked: EventTypeAccountContactBlocked indicates the payload includes that the account has blocked a contact\n - EventTypeAccountContactUnblocked: EventTypeAccountContactUnblocked indicates the payload includes that the account has unblocked a contact\n - EventTypeContactAliasKeyAdded: EventTypeContactAliasKeyAdded indicates the payload includes that the contact group has received an alias key\n - EventTypeMultiMemberGroupAliasResolverAdded: EventTypeMultiMemberGroupAliasResolverAdded indicates the payload includes that a member of the group sent their alias proof\n - EventTypeMultiMemberGroupInitialMemberAnnounced: EventTypeMultiMemberGroupInitialMemberAnnounced indicates the payload includes that a member has authenticated themselves as the group owner\n - EventTypeMultiMemberGroupAdminRoleGranted: EventTypeMultiMemberGroupAdminRoleGranted indicates the payload includes that an admin of the group granted another member as an admin\n - EventTypeAccountServiceTokenAdded: EventTypeAccountServiceTokenAdded indicates that a new service provider has been registered for this account\n - EventTypeAccountServiceTokenRemoved: EventTypeAccountServiceTokenRemoved indicates that a service provider is not available anymore\n - EventTypeGroupReplicating: EventTypeGroupReplicating indicates that the group has been registered for replication on a server\n - EventTypePushMemberTokenUpdate: EventTypePushMemberTokenUpdate\n - EventTypePushDeviceTokenRegistered: EventTypePushDeviceTokenRegistered\n - EventTypePushDeviceServerRegistered: EventTypePushDeviceServerRegistered\n - EventTypeAccountVerifiedCredentialRegistered: EventTypeAccountVerifiedCredentialRegistered\n - EventTypeGroupMetadataPayloadSent: EventTypeGroupMetadataPayloadSent indicates the payload includes an app specific event, unlike messages stored on the message store it is encrypted using a static key" }, "v1Group": { "type": "object", @@ -586,7 +586,7 @@ }, "group_type": { "$ref": "#/definitions/v1GroupType", - "title": "group_type specifies the type of the group, used to determine how device secrets are generated" + "title": "group_type specifies the type of the group, used to determine how device chain key is generated" }, "sign_pub": { "type": "string", @@ -718,22 +718,6 @@ "default": "GroupTypeUndefined", "description": " - GroupTypeUndefined: GroupTypeUndefined indicates that the value has not been set. For example, happens if group is replicated.\n - GroupTypeAccount: GroupTypeAccount is the group managing an account, available to all its devices.\n - GroupTypeContact: GroupTypeContact is the group created between two accounts, available to all their devices.\n - GroupTypeMultiMember: GroupTypeMultiMember is a group containing an undefined number of members." }, - "v1MemberWithDevices": { - "type": "object", - "properties": { - "member_pk": { - "type": "string", - "format": "byte" - }, - "devices_pks": { - "type": "array", - "items": { - "type": "string", - "format": "byte" - } - } - } - }, "v1MessageHeaders": { "type": "object", "properties": { @@ -826,6 +810,34 @@ } } }, + "v1OutOfStoreReceiveReply": { + "type": "object", + "properties": { + "message": { + "$ref": "#/definitions/v1OutOfStoreMessage" + }, + "cleartext": { + "type": "string", + "format": "byte" + }, + "group_public_key": { + "type": "string", + "format": "byte" + }, + "already_received": { + "type": "boolean" + } + } + }, + "v1OutOfStoreSealReply": { + "type": "object", + "properties": { + "encrypted": { + "type": "string", + "format": "byte" + } + } + }, "v1PeerListPeer": { "type": "object", "properties": { @@ -883,36 +895,6 @@ "v1ProtocolMetadata": { "type": "object" }, - "v1PushReceiveReply": { - "type": "object", - "properties": { - "message": { - "$ref": "#/definitions/v1OutOfStoreMessage" - }, - "cleartext": { - "type": "string", - "format": "byte" - }, - "group_public_key": { - "type": "string", - "format": "byte" - }, - "already_received": { - "type": "boolean" - } - } - }, - "v1PushSendReply": { - "type": "object", - "properties": { - "group_members": { - "type": "array", - "items": { - "$ref": "#/definitions/v1MemberWithDevices" - } - } - } - }, "v1PushServer": { "type": "object", "properties": { @@ -962,15 +944,6 @@ "default": "PushTokenUndefined", "title": "- PushTokenMQTT: PushTokenMQTT: Platform independent\n - PushTokenApplePushNotificationService: PushTokenApplePushNotificationService: iOS, iPadOS, tvOS, macOS\n - PushTokenFirebaseCloudMessaging: PushTokenFirebaseCloudMessaging: Android with GMS, Chrome OS\n - PushTokenWindowsPushNotificationService: PushTokenWindowsPushNotificationService: Windows, XBox\n - PushTokenHuaweiPushKit: PushTokenHuaweiPushKit: Huawei Android devices with AppGallery\n - PushTokenAmazonDeviceMessaging: PushTokenAmazonDeviceMessaging: Fire OS devices" }, - "v1PushSetDeviceTokenReply": { - "type": "object" - }, - "v1PushSetServerReply": { - "type": "object" - }, - "v1PushShareTokenReply": { - "type": "object" - }, "v1RefreshContactRequestPeer": { "type": "object", "properties": { diff --git a/events.go b/events.go index 0e441893..c87d5485 100644 --- a/events.go +++ b/events.go @@ -18,7 +18,7 @@ var eventTypesMapper = map[protocoltypes.EventType]struct { SigChecker sigChecker }{ protocoltypes.EventTypeGroupMemberDeviceAdded: {Message: &protocoltypes.GroupAddMemberDevice{}, SigChecker: sigCheckerMemberDeviceAdded}, - protocoltypes.EventTypeGroupDeviceSecretAdded: {Message: &protocoltypes.GroupAddDeviceSecret{}, SigChecker: sigCheckerDeviceSigned}, + protocoltypes.EventTypeGroupDeviceChainKeyAdded: {Message: &protocoltypes.GroupAddDeviceChainKey{}, SigChecker: sigCheckerDeviceSigned}, protocoltypes.EventTypeAccountGroupJoined: {Message: &protocoltypes.AccountGroupJoined{}, SigChecker: sigCheckerDeviceSigned}, protocoltypes.EventTypeAccountGroupLeft: {Message: &protocoltypes.AccountGroupLeft{}, SigChecker: sigCheckerDeviceSigned}, protocoltypes.EventTypeAccountContactRequestDisabled: {Message: &protocoltypes.AccountContactRequestDisabled{}, SigChecker: sigCheckerDeviceSigned}, @@ -98,7 +98,7 @@ func newGroupMetadataEventFromEntry(_ ipfslog.Log, e ipfslog.Entry, metadata *pr } // TODO(gfanton): getParentsCID use a lot of ressources, disable it until we need it - // evtCtx := newEventContext(e.GetHash(), getParentsForCID(log, e.GetHash()), g, attachmentsCIDs) + // evtCtx := newEventContext(e.GetHash(), getParentsForCID(log, e.GetHash()), group, attachmentsCIDs) evtCtx := newEventContext(e.GetHash(), []cid.Cid{}, g) gme := protocoltypes.GroupMetadataEvent{ @@ -121,7 +121,7 @@ func openGroupEnvelope(g *protocoltypes.Group, envelopeBytes []byte) (*protocolt return nil, nil, errcode.ErrSerialization.Wrap(err) } - data, ok := secretbox.Open(nil, env.Event, nonce, cryptoutil.GetSharedSecret(g)) + data, ok := secretbox.Open(nil, env.Event, nonce, g.GetSharedSecret()) if !ok { return nil, nil, errcode.ErrGroupMemberLogEventOpen } @@ -173,7 +173,7 @@ func sealGroupEnvelope(g *protocoltypes.Group, eventType protocoltypes.EventType return nil, errcode.ErrSerialization.Wrap(err) } - eventBytes := secretbox.Seal(nil, eventClearBytes, nonce, cryptoutil.GetSharedSecret(g)) + eventBytes := secretbox.Seal(nil, eventClearBytes, nonce, g.GetSharedSecret()) env := &protocoltypes.GroupEnvelope{ Event: eventBytes, diff --git a/gen.sum b/gen.sum index 5a288283..e3686c0e 100644 --- a/gen.sum +++ b/gen.sum @@ -1 +1 @@ -bc99bdec856c9105662f7985416c343767e8d652 Makefile +6b8c13153a0ba512493dcc98cbf8217aba3fe330 Makefile diff --git a/group.go b/group.go index e8e2aa5f..dcbb63a1 100644 --- a/group.go +++ b/group.go @@ -1,12 +1,8 @@ package weshnet import ( - crand "crypto/rand" - "github.com/libp2p/go-libp2p/core/crypto" - "golang.org/x/crypto/nacl/box" - "berty.tech/weshnet/pkg/cryptoutil" "berty.tech/weshnet/pkg/errcode" "berty.tech/weshnet/pkg/protocoltypes" ) @@ -16,59 +12,15 @@ const CurrentGroupVersion = 1 // NewGroupMultiMember creates a new Group object and an invitation to be used by // the first member of the group func NewGroupMultiMember() (*protocoltypes.Group, crypto.PrivKey, error) { - priv, pub, err := crypto.GenerateEd25519Key(crand.Reader) - if err != nil { - return nil, nil, errcode.ErrCryptoKeyGeneration.Wrap(err) - } - - pubBytes, err := pub.Raw() - if err != nil { - return nil, nil, errcode.ErrSerialization.Wrap(err) - } - - signing, _, err := crypto.GenerateEd25519Key(crand.Reader) - if err != nil { - return nil, nil, errcode.ErrCryptoKeyGeneration.Wrap(err) - } - - signingBytes, err := cryptoutil.SeedFromEd25519PrivateKey(signing) - if err != nil { - return nil, nil, errcode.ErrSerialization.Wrap(err) - } - - skSig, err := priv.Sign(signingBytes) - if err != nil { - return nil, nil, errcode.ErrCryptoSignature.Wrap(err) - } - - group := &protocoltypes.Group{ - PublicKey: pubBytes, - Secret: signingBytes, - SecretSig: skSig, - GroupType: protocoltypes.GroupTypeMultiMember, - } - - updateKey, err := cryptoutil.GetLinkKeyArray(group) - if err != nil { - return nil, nil, errcode.ErrCryptoKeyGeneration.Wrap(err) - } - - linkKeySig, err := priv.Sign(updateKey[:]) - if err != nil { - return nil, nil, errcode.ErrCryptoSignature.Wrap(err) - } - - group.LinkKeySig = linkKeySig - - return group, priv, nil + return protocoltypes.NewGroupMultiMember() } -func openDeviceSecret(m *protocoltypes.GroupMetadata, localMemberPrivateKey crypto.PrivKey, group *protocoltypes.Group) (crypto.PubKey, *protocoltypes.DeviceSecret, error) { - if m == nil || m.EventType != protocoltypes.EventTypeGroupDeviceSecretAdded { +func getAndFilterGroupAddDeviceChainKeyPayload(m *protocoltypes.GroupMetadata, localMemberPublicKey crypto.PubKey) (crypto.PubKey, []byte, error) { + if m == nil || m.EventType != protocoltypes.EventTypeGroupDeviceChainKeyAdded { return nil, nil, errcode.ErrInvalidInput } - s := &protocoltypes.GroupAddDeviceSecret{} + s := &protocoltypes.GroupAddDeviceChainKey{} if err := s.Unmarshal(m.Payload); err != nil { return nil, nil, errcode.ErrDeserialization.Wrap(err) } @@ -83,44 +35,9 @@ func openDeviceSecret(m *protocoltypes.GroupMetadata, localMemberPrivateKey cryp return nil, nil, errcode.ErrDeserialization.Wrap(err) } - if !localMemberPrivateKey.GetPublic().Equals(destMemberPubKey) { + if !localMemberPublicKey.Equals(destMemberPubKey) { return nil, nil, errcode.ErrGroupSecretOtherDestMember } - mongPriv, mongPub, err := cryptoutil.EdwardsToMontgomery(localMemberPrivateKey, senderDevicePubKey) - if err != nil { - return nil, nil, errcode.ErrCryptoKeyConversion.Wrap(err) - } - - nonce := groupIDToNonce(group) - decryptedSecret := &protocoltypes.DeviceSecret{} - decryptedMessage, ok := box.Open(nil, s.Payload, nonce, mongPub, mongPriv) - if !ok { - return nil, nil, errcode.ErrCryptoDecrypt - } - - err = decryptedSecret.Unmarshal(decryptedMessage) - if err != nil { - return nil, nil, errcode.ErrDeserialization - } - - return senderDevicePubKey, decryptedSecret, nil -} - -func groupIDToNonce(group *protocoltypes.Group) *[cryptoutil.NonceSize]byte { - // Nonce doesn't need to be secret, random nor unpredictable, it just needs - // to be used only once for a given {sender, receiver} set and we will send - // only one SecretEntryPayload per {localDevicePrivKey, remoteMemberPubKey} - // So we can reuse groupID as nonce for all SecretEntryPayload and save - // 24 bytes of storage and bandwidth for each of them. - // - // See https://pynacl.readthedocs.io/en/stable/secret/#nonce - // See Security Model here: https://nacl.cr.yp.to/box.html - var nonce [cryptoutil.NonceSize]byte - - gid := group.GetPublicKey() - - copy(nonce[:], gid) - - return &nonce + return senderDevicePubKey, s.Payload, nil } diff --git a/group_context.go b/group_context.go index 8f0f748d..c784fd50 100644 --- a/group_context.go +++ b/group_context.go @@ -12,11 +12,11 @@ import ( "go.uber.org/zap" "berty.tech/go-orbit-db/stores" - "berty.tech/weshnet/pkg/cryptoutil" "berty.tech/weshnet/pkg/errcode" "berty.tech/weshnet/pkg/ipfsutil" "berty.tech/weshnet/pkg/logutil" "berty.tech/weshnet/pkg/protocoltypes" + "berty.tech/weshnet/pkg/secretstore" ) type GroupContext struct { @@ -25,18 +25,14 @@ type GroupContext struct { group *protocoltypes.Group metadataStore *MetadataStore messageStore *MessageStore - messageKeystore *cryptoutil.MessageKeystore - memberDevice *cryptoutil.OwnMemberDevice + secretStore secretstore.SecretStore + ownMemberDevice secretstore.OwnMemberDevice logger *zap.Logger closed uint32 } -func (gc *GroupContext) MessageKeystore() *cryptoutil.MessageKeystore { - return gc.messageKeystore -} - -func (gc *GroupContext) getMemberPrivKey() crypto.PrivKey { - return gc.memberDevice.PrivateMember() +func (gc *GroupContext) SecretStore() secretstore.SecretStore { + return gc.secretStore } func (gc *GroupContext) MessageStore() *MessageStore { @@ -52,11 +48,11 @@ func (gc *GroupContext) Group() *protocoltypes.Group { } func (gc *GroupContext) MemberPubKey() crypto.PubKey { - return gc.memberDevice.PrivateMember().GetPublic() + return gc.ownMemberDevice.Member() } func (gc *GroupContext) DevicePubKey() crypto.PubKey { - return gc.memberDevice.PrivateDevice().GetPublic() + return gc.ownMemberDevice.Device() } func (gc *GroupContext) Close() error { @@ -76,7 +72,7 @@ func (gc *GroupContext) IsClosed() bool { return atomic.LoadUint32(&gc.closed) != 0 } -func NewContextGroup(group *protocoltypes.Group, metadataStore *MetadataStore, messageStore *MessageStore, messageKeystore *cryptoutil.MessageKeystore, memberDevice *cryptoutil.OwnMemberDevice, logger *zap.Logger) *GroupContext { +func NewContextGroup(group *protocoltypes.Group, metadataStore *MetadataStore, messageStore *MessageStore, secretStore secretstore.SecretStore, memberDevice secretstore.OwnMemberDevice, logger *zap.Logger) *GroupContext { ctx, cancel := context.WithCancel(context.Background()) if logger == nil { @@ -89,8 +85,8 @@ func NewContextGroup(group *protocoltypes.Group, metadataStore *MetadataStore, m group: group, metadataStore: metadataStore, messageStore: messageStore, - messageKeystore: messageKeystore, - memberDevice: memberDevice, + secretStore: secretStore, + ownMemberDevice: memberDevice, logger: logger.With(logutil.PrivateString("group-id", fmt.Sprintf("%.6s", base64.StdEncoding.EncodeToString(group.PublicKey)))), closed: 0, } @@ -112,7 +108,7 @@ func (gc *GroupContext) activateGroupContext(contact crypto.PubKey, selfAnnounce chNewData := gc.FillMessageKeysHolderUsingNewData() go func() { for pk := range chNewData { - if !pk.Equals(gc.memberDevice.PrivateDevice().GetPublic()) { + if !pk.Equals(gc.ownMemberDevice.Device()) { gc.logger.Warn("gc member device public key doesn't match") } } @@ -122,7 +118,7 @@ func (gc *GroupContext) activateGroupContext(contact crypto.PubKey, selfAnnounce go func() { calledDone := false for pk := range chMember { - if !pk.Equals(gc.memberDevice.PrivateMember().GetPublic()) { + if !pk.Equals(gc.ownMemberDevice.Member()) { gc.logger.Warn("gc member device public key doesn't match") continue } @@ -143,7 +139,7 @@ func (gc *GroupContext) activateGroupContext(contact crypto.PubKey, selfAnnounce chPreviousData := gc.FillMessageKeysHolderUsingPreviousData() go func() { for pk := range chPreviousData { - if !pk.Equals(gc.memberDevice.PrivateDevice().GetPublic()) { + if !pk.Equals(gc.ownMemberDevice.Device()) { gc.logger.Warn("gc member device public key doesn't match") } } @@ -155,7 +151,7 @@ func (gc *GroupContext) activateGroupContext(contact crypto.PubKey, selfAnnounce chSecrets := gc.SendSecretsToExistingMembers(contact) go func() { for pk := range chSecrets { - if !pk.Equals(gc.memberDevice.PrivateMember().GetPublic()) { + if !pk.Equals(gc.ownMemberDevice.Member()) { gc.logger.Warn("gc member device public key doesn't match") } } @@ -215,11 +211,11 @@ func (gc *GroupContext) FillMessageKeysHolderUsingNewData() <-chan crypto.PubKey } e := evt.(protocoltypes.GroupMetadataEvent) - if e.Metadata.EventType != protocoltypes.EventTypeGroupDeviceSecretAdded { + if e.Metadata.EventType != protocoltypes.EventTypeGroupDeviceChainKeyAdded { continue } - pk, ds, err := openDeviceSecret(e.Metadata, gc.getMemberPrivKey(), gc.Group()) + senderPublicKey, encryptedDeviceChainKey, err := getAndFilterGroupAddDeviceChainKeyPayload(e.Metadata, gc.ownMemberDevice.Member()) if errcode.Is(err, errcode.ErrInvalidInput) { continue } @@ -229,21 +225,21 @@ func (gc *GroupContext) FillMessageKeysHolderUsingNewData() <-chan crypto.PubKey } if err != nil { - gc.logger.Error("an error occurred while opening device secrets", zap.Error(err)) + gc.logger.Error("an error occurred while opening device chain key", zap.Error(err)) continue } - if err = gc.MessageKeystore().RegisterChainKey(gc.ctx, gc.Group(), pk, ds, gc.DevicePubKey().Equals(pk)); err != nil { + if err = gc.SecretStore().RegisterChainKey(gc.ctx, gc.Group(), senderPublicKey, encryptedDeviceChainKey); err != nil { gc.logger.Error("unable to register chain key", zap.Error(err)) continue } // A new chainKey is registered, check if cached messages can be opened with it - if rawPK, err := pk.Raw(); err == nil { + if rawPK, err := senderPublicKey.Raw(); err == nil { gc.MessageStore().ProcessMessageQueueForDevicePK(gc.ctx, rawPK) } - ch <- pk + ch <- senderPublicKey } }() @@ -305,17 +301,17 @@ func (gc *GroupContext) FillMessageKeysHolderUsingPreviousData() <-chan crypto.P publishedSecrets := gc.metadataStoreListSecrets() go func() { - for pk, sec := range publishedSecrets { - if err := gc.MessageKeystore().RegisterChainKey(gc.ctx, gc.Group(), pk, sec, gc.DevicePubKey().Equals(pk)); err != nil { + for senderPublicKey, encryptedSecret := range publishedSecrets { + if err := gc.SecretStore().RegisterChainKey(gc.ctx, gc.Group(), senderPublicKey, encryptedSecret); err != nil { gc.logger.Error("unable to register chain key", zap.Error(err)) continue } // A new chainKey is registered, check if cached messages can be opened with it - if rawPK, err := pk.Raw(); err == nil { + if rawPK, err := senderPublicKey.Raw(); err == nil { gc.MessageStore().ProcessMessageQueueForDevicePK(gc.ctx, rawPK) } - ch <- pk + ch <- senderPublicKey } close(ch) @@ -324,13 +320,10 @@ func (gc *GroupContext) FillMessageKeysHolderUsingPreviousData() <-chan crypto.P return ch } -func (gc *GroupContext) metadataStoreListSecrets() map[crypto.PubKey]*protocoltypes.DeviceSecret { - publishedSecrets := map[crypto.PubKey]*protocoltypes.DeviceSecret{} +func (gc *GroupContext) metadataStoreListSecrets() map[crypto.PubKey][]byte { + publishedSecrets := map[crypto.PubKey][]byte{} m := gc.MetadataStore() - ownSK := gc.getMemberPrivKey() - g := gc.Group() - metadatas, err := m.ListEvents(gc.ctx, nil, nil, false) if err != nil { return nil @@ -340,17 +333,17 @@ func (gc *GroupContext) metadataStoreListSecrets() map[crypto.PubKey]*protocolty continue } - pk, ds, err := openDeviceSecret(metadata.Metadata, ownSK, g) + pk, encryptedDeviceChainKey, err := getAndFilterGroupAddDeviceChainKeyPayload(metadata.Metadata, gc.MemberPubKey()) if errcode.Is(err, errcode.ErrInvalidInput) || errcode.Is(err, errcode.ErrGroupSecretOtherDestMember) { continue } if err != nil { - gc.logger.Error("unable to open device secret", zap.Error(err)) + gc.logger.Error("unable to open device chain key", zap.Error(err)) continue } - publishedSecrets[pk] = ds + publishedSecrets[pk] = encryptedDeviceChainKey } return publishedSecrets diff --git a/group_test.go b/group_test.go deleted file mode 100644 index fbe95f3a..00000000 --- a/group_test.go +++ /dev/null @@ -1,36 +0,0 @@ -package weshnet - -import ( - crand "crypto/rand" - "testing" - - "github.com/libp2p/go-libp2p/core/crypto" - "github.com/stretchr/testify/require" - - "berty.tech/weshnet/pkg/cryptoutil" - "berty.tech/weshnet/pkg/protocoltypes" -) - -func TestGetGroupForContact(t *testing.T) { - sk, _, err := crypto.GenerateEd25519Key(crand.Reader) - require.NoError(t, err) - - g, err := cryptoutil.GetGroupForContact(sk) - require.NoError(t, err) - - require.Equal(t, g.GroupType, protocoltypes.GroupTypeContact) - require.Equal(t, len(g.PublicKey), 32) - require.Equal(t, len(g.Secret), 32) -} - -func TestGetKeysForGroupOfContact(t *testing.T) { - sk, _, err := crypto.GenerateEd25519Key(crand.Reader) - require.NoError(t, err) - - sk1, sk2, err := cryptoutil.GetKeysForGroupOfContact(sk) - require.NoError(t, err) - - require.NotNil(t, sk1) - require.NotNil(t, sk2) - require.False(t, sk1.Equals(sk2)) -} diff --git a/iface_account.go b/iface_account.go index 099bdcb3..945eec8f 100644 --- a/iface_account.go +++ b/iface_account.go @@ -1,15 +1,10 @@ package weshnet import ( - crand "crypto/rand" - "math" - "math/big" - "github.com/libp2p/go-libp2p/core/crypto" - "berty.tech/weshnet/pkg/cryptoutil" - "berty.tech/weshnet/pkg/errcode" "berty.tech/weshnet/pkg/protocoltypes" + "berty.tech/weshnet/pkg/secretstore" ) type AccountKeys interface { @@ -17,30 +12,5 @@ type AccountKeys interface { AccountProofPrivKey() (crypto.PrivKey, error) DevicePrivKey() (crypto.PrivKey, error) ContactGroupPrivKey(pk crypto.PubKey) (crypto.PrivKey, error) - MemberDeviceForGroup(g *protocoltypes.Group) (*cryptoutil.OwnMemberDevice, error) -} - -// MemberDevice is a remote device part of a group -type MemberDevice struct { - Member crypto.PubKey - Device crypto.PubKey - Secret *protocoltypes.DeviceSecret -} - -func NewDeviceSecret() (*protocoltypes.DeviceSecret, error) { - counter, err := crand.Int(crand.Reader, big.NewInt(0).SetUint64(math.MaxUint64)) - if err != nil { - return nil, errcode.ErrCryptoRandomGeneration.Wrap(err) - } - - chainKey := make([]byte, 32) - _, err = crand.Read(chainKey) - if err != nil { - return nil, errcode.ErrCryptoRandomGeneration.Wrap(err) - } - - return &protocoltypes.DeviceSecret{ - ChainKey: chainKey, - Counter: counter.Uint64(), - }, nil + MemberDeviceForGroup(g *protocoltypes.Group) (secretstore.OwnMemberDevice, error) } diff --git a/internal/datastoreutil/consts.go b/internal/datastoreutil/consts.go index 919d2b98..ea63efd1 100644 --- a/internal/datastoreutil/consts.go +++ b/internal/datastoreutil/consts.go @@ -1,7 +1,5 @@ package datastoreutil const ( - AccountCacheDatastorePushServerPK = "push_server_public_key" - NamespaceAccountCacheDatastore = "account_cache_datastore" - NamespaceMessageKeystore = "messages_keystore" + NamespaceMessageKeystore = "messages_keystore" ) diff --git a/message_marshaler.go b/message_marshaler.go index fd4df01c..783ba121 100644 --- a/message_marshaler.go +++ b/message_marshaler.go @@ -12,9 +12,9 @@ import ( "berty.tech/go-ipfs-log/enc" "berty.tech/go-ipfs-log/entry" "berty.tech/go-orbit-db/iface" - "berty.tech/weshnet/pkg/cryptoutil" "berty.tech/weshnet/pkg/protocoltypes" "berty.tech/weshnet/pkg/rendezvous" + "berty.tech/weshnet/pkg/secretstore" ) type PeerDeviceGroup struct { @@ -29,20 +29,20 @@ type OrbitDBMessageMarshaler struct { deviceCaches map[peer.ID]*PeerDeviceGroup muMarshall sync.RWMutex selfid peer.ID - dk cryptoutil.DeviceKeystore + secretStore secretstore.SecretStore // in Replication Mode DeviceKey should not be sent useReplicationMode bool } -func NewOrbitDBMessageMarshaler(selfid peer.ID, dk cryptoutil.DeviceKeystore, rp *rendezvous.RotationInterval, useReplicationMode bool) *OrbitDBMessageMarshaler { +func NewOrbitDBMessageMarshaler(selfid peer.ID, secretStore secretstore.SecretStore, rp *rendezvous.RotationInterval, useReplicationMode bool) *OrbitDBMessageMarshaler { return &OrbitDBMessageMarshaler{ selfid: selfid, sharedKeys: make(map[string]enc.SharedKey), deviceCaches: make(map[peer.ID]*PeerDeviceGroup), topicGroup: make(map[string]*protocoltypes.Group), rp: rp, - dk: dk, + secretStore: secretStore, useReplicationMode: useReplicationMode, } } @@ -94,12 +94,12 @@ func (m *OrbitDBMessageMarshaler) Marshal(msg *iface.MessageExchangeHeads) ([]by // in replication mode, it doesn't make sense to send DevicePK if !m.useReplicationMode { - ownDevice, err := m.dk.MemberDeviceForGroup(group) + ownDevice, err := m.secretStore.GetOwnMemberDeviceForGroup(group) if err != nil { return nil, fmt.Errorf("unable to get own member device key for group: %w", err) } - ownPK, err = ownDevice.PrivateDevice().GetPublic().Raw() + ownPK, err = ownDevice.Device().Raw() if err != nil { return nil, fmt.Errorf("unable to get raw pk for device: %w", err) } diff --git a/message_marshaler_test.go b/message_marshaler_test.go index 4c58fa0d..4c761619 100644 --- a/message_marshaler_test.go +++ b/message_marshaler_test.go @@ -4,7 +4,6 @@ import ( "testing" "time" - keystore "github.com/ipfs/go-ipfs-keystore" mocknet "github.com/libp2p/go-libp2p/p2p/net/mock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -12,8 +11,8 @@ import ( "berty.tech/go-ipfs-log/enc" "berty.tech/go-ipfs-log/entry" "berty.tech/go-orbit-db/iface" - "berty.tech/weshnet/pkg/cryptoutil" "berty.tech/weshnet/pkg/rendezvous" + "berty.tech/weshnet/pkg/secretstore" ) var ( @@ -37,8 +36,8 @@ func TestRotationMessageMarshaler(t *testing.T) { require.NoError(t, err) // generate keystore - ks1 := keystore.NewMemKeystore() - acc1 := cryptoutil.NewDeviceKeystore(ks1, nil) + acc1, err := secretstore.NewInMemSecretStore(nil) + require.NoError(t, err) rp := rendezvous.NewStaticRotationInterval() m := NewOrbitDBMessageMarshaler(p.ID(), acc1, rp, false) @@ -76,8 +75,8 @@ func TestRotationMessageMarshalUnknownTopic(t *testing.T) { require.NoError(t, err) // generate keystore - ks := keystore.NewMemKeystore() - acc := cryptoutil.NewDeviceKeystore(ks, nil) + acc, err := secretstore.NewInMemSecretStore(nil) + require.NoError(t, err) rp := rendezvous.NewStaticRotationInterval() m := NewOrbitDBMessageMarshaler(p.ID(), acc, rp, false) @@ -109,11 +108,11 @@ func TestRotationMessageUnmarshalUnknownTopic(t *testing.T) { require.NoError(t, err) // generate keystore - ks1 := keystore.NewMemKeystore() - acc1 := cryptoutil.NewDeviceKeystore(ks1, nil) + acc1, err := secretstore.NewInMemSecretStore(nil) + require.NoError(t, err) - ks2 := keystore.NewMemKeystore() - acc2 := cryptoutil.NewDeviceKeystore(ks2, nil) + acc2, err := secretstore.NewInMemSecretStore(nil) + require.NoError(t, err) g1, _, err := NewGroupMultiMember() require.NoError(t, err) @@ -175,11 +174,11 @@ func TestRotationMessageMarshalWrongKey(t *testing.T) { require.NoError(t, err) // generate keystore - ks1 := keystore.NewMemKeystore() - acc1 := cryptoutil.NewDeviceKeystore(ks1, nil) + acc1, err := secretstore.NewInMemSecretStore(nil) + require.NoError(t, err) - ks2 := keystore.NewMemKeystore() - acc2 := cryptoutil.NewDeviceKeystore(ks2, nil) + acc2, err := secretstore.NewInMemSecretStore(nil) + require.NoError(t, err) rp1 := rendezvous.NewStaticRotationInterval() rp1.RegisterRotation(time.Now(), msg.Address, testSeed1) diff --git a/orbitdb.go b/orbitdb.go index 6513b477..e7c20bde 100644 --- a/orbitdb.go +++ b/orbitdb.go @@ -27,12 +27,11 @@ import ( "berty.tech/go-orbit-db/iface" "berty.tech/go-orbit-db/pubsub/pubsubcoreapi" "berty.tech/go-orbit-db/stores" - "berty.tech/weshnet/internal/datastoreutil" - "berty.tech/weshnet/pkg/cryptoutil" "berty.tech/weshnet/pkg/errcode" "berty.tech/weshnet/pkg/ipfsutil" "berty.tech/weshnet/pkg/protocoltypes" "berty.tech/weshnet/pkg/rendezvous" + "berty.tech/weshnet/pkg/secretstore" "berty.tech/weshnet/pkg/tyber" ) @@ -53,13 +52,12 @@ type loggable interface { type NewOrbitDBOptions struct { baseorbitdb.NewOrbitDBOptions Datastore datastore.Batching - MessageKeystore *cryptoutil.MessageKeystore - DeviceKeystore cryptoutil.DeviceKeystore + SecretStore secretstore.SecretStore RotationInterval *rendezvous.RotationInterval - ReplicationMode bool GroupMetadataStoreType string GroupMessageStoreType string + ReplicationMode bool } func (n *NewOrbitDBOptions) applyDefaults() { @@ -75,14 +73,6 @@ func (n *NewOrbitDBOptions) applyDefaults() { n.Logger = zap.NewNop() } - if n.MessageKeystore == nil { - n.MessageKeystore = cryptoutil.NewMessageKeystore(datastoreutil.NewNamespacedDatastore(n.Datastore, datastore.NewKey(datastoreutil.NamespaceMessageKeystore)), n.Logger) - } - - if n.DeviceKeystore == nil { - n.DeviceKeystore = cryptoutil.NewDeviceKeystore(ipfsutil.NewDatastoreKeystore(datastoreutil.NewNamespacedDatastore(n.Datastore, datastore.NewKey(NamespaceDeviceKeystore))), nil) - } - if n.RotationInterval == nil { n.RotationInterval = rendezvous.NewStaticRotationInterval() } @@ -110,11 +100,11 @@ type ( type WeshOrbitDB struct { baseorbitdb.BaseOrbitDB keyStore *BertySignedKeyStore - messageKeystore *cryptoutil.MessageKeystore - deviceKeystore cryptoutil.DeviceKeystore + secretStore secretstore.SecretStore pubSub iface.PubSubInterface rotationInterval *rendezvous.RotationInterval messageMarshaler *OrbitDBMessageMarshaler + replicationMode bool groupMetadataStoreType string groupMessageStoreType string @@ -189,7 +179,7 @@ func NewWeshOrbitDB(ctx context.Context, ipfs coreapi.CoreAPI, options *NewOrbit options.PubSub = pubsubcoreapi.NewPubSub(ipfs, self.ID(), time.Second, options.Logger, options.Tracer) } - mm := NewOrbitDBMessageMarshaler(self.ID(), options.DeviceKeystore, options.RotationInterval, options.ReplicationMode) + mm := NewOrbitDBMessageMarshaler(self.ID(), options.SecretStore, options.RotationInterval, options.ReplicationMode) options.MessageMarshaler = mm orbitDB, err := baseorbitdb.NewOrbitDB(ctx, ipfs, &options.NewOrbitDBOptions) @@ -198,20 +188,19 @@ func NewWeshOrbitDB(ctx context.Context, ipfs coreapi.CoreAPI, options *NewOrbit } bertyDB := &WeshOrbitDB{ - ctx: ctx, - messageMarshaler: mm, - BaseOrbitDB: orbitDB, - keyStore: ks, - deviceKeystore: options.DeviceKeystore, - messageKeystore: options.MessageKeystore, - rotationInterval: options.RotationInterval, - pubSub: options.PubSub, - groups: &GroupMap{}, - groupContexts: &GroupContextMap{}, // map[string]*GroupContext - groupsSigPubKey: &GroupsSigPubKeyMap{}, // map[string]crypto.PubKey - + ctx: ctx, + messageMarshaler: mm, + BaseOrbitDB: orbitDB, + keyStore: ks, + secretStore: options.SecretStore, + rotationInterval: options.RotationInterval, + pubSub: options.PubSub, + groups: &GroupMap{}, + groupContexts: &GroupContextMap{}, // map[string]*GroupContext + groupsSigPubKey: &GroupsSigPubKeyMap{}, // map[string]crypto.PubKey groupMetadataStoreType: options.GroupMetadataStoreType, groupMessageStoreType: options.GroupMessageStoreType, + replicationMode: options.ReplicationMode, } if err := bertyDB.RegisterAccessControllerType(NewSimpleAccessController); err != nil { @@ -234,34 +223,21 @@ func (s *WeshOrbitDB) openAccountGroup(ctx context.Context, options *orbitdb.Cre options.EventBus = s.EventBus() } - sk, err := s.deviceKeystore.AccountPrivKey() - if err != nil { - return nil, errcode.ErrOrbitDBOpen.Wrap(err) - } - l.Debug("Got AccountPrivKey", tyber.FormatStepLogFields(ctx, []tyber.Detail{})...) - - skProof, err := s.deviceKeystore.AccountProofPrivKey() - if err != nil { - return nil, errcode.ErrOrbitDBOpen.Wrap(err) - } - - l.Debug("Got AccountProofPrivKey", tyber.FormatStepLogFields(ctx, []tyber.Detail{})...) - - g, err := cryptoutil.GetGroupForAccount(sk, skProof) + group, _, err := s.secretStore.GetGroupForAccount() if err != nil { return nil, errcode.ErrOrbitDBOpen.Wrap(err) } - l.Debug("Got account group", tyber.FormatStepLogFields(ctx, []tyber.Detail{{Name: "Group", Description: g.String()}})...) + l.Debug("Got account group", tyber.FormatStepLogFields(ctx, []tyber.Detail{{Name: "Group", Description: group.String()}})...) - gc, err := s.OpenGroup(ctx, g, options) + gc, err := s.OpenGroup(ctx, group, options) if err != nil { return nil, errcode.ErrGroupOpen.Wrap(err) } l.Debug("Opened account group", tyber.FormatStepLogFields(ctx, []tyber.Detail{})...) - if err := gc.ActivateGroupContext(nil); err != nil { + if err := gc.ActivateGroupContext(gc.ownMemberDevice.Member()); err != nil { return nil, errcode.TODO.Wrap(err) } @@ -400,7 +376,7 @@ func (s *WeshOrbitDB) loadHeads(ctx context.Context, store iface.Store, heads [] } func (s *WeshOrbitDB) OpenGroup(ctx context.Context, g *protocoltypes.Group, options *orbitdb.CreateDBOptions) (*GroupContext, error) { - if s.deviceKeystore == nil || s.messageKeystore == nil { + if s.secretStore == nil { return nil, errcode.ErrInvalidInput.Wrap(fmt.Errorf("db open in naive mode")) } @@ -423,23 +399,23 @@ func (s *WeshOrbitDB) OpenGroup(ctx context.Context, g *protocoltypes.Group, opt s.Logger().Debug("OpenGroup", tyber.FormatStepLogFields(s.ctx, tyber.ZapFieldsToDetails(zap.Any("public key", g.PublicKey), zap.Any("secret", g.Secret), zap.Stringer("type", g.GroupType)))...) - memberDevice, err := s.deviceKeystore.MemberDeviceForGroup(g) + memberDevice, err := s.secretStore.GetOwnMemberDeviceForGroup(g) if err != nil { return nil, errcode.ErrCryptoKeyGeneration.Wrap(err) } - mpkb, err := crypto.MarshalPublicKey(memberDevice.Public().Member) + mpkb, err := crypto.MarshalPublicKey(memberDevice.Member()) if err != nil { mpkb = []byte{} } s.Logger().Debug("Got member device", tyber.FormatStepLogFields(s.ctx, []tyber.Detail{{Name: "DevicePublicKey", Description: base64.RawURLEncoding.EncodeToString(mpkb)}})...) // Force secret generation if missing - if _, err := s.messageKeystore.GetDeviceSecret(s.ctx, g, s.deviceKeystore); err != nil { + if _, err := s.secretStore.GetShareableChainKey(s.ctx, g, memberDevice.Member()); err != nil { return nil, errcode.ErrCryptoKeyGeneration.Wrap(err) } - s.Logger().Debug("Got device secret", tyber.FormatStepLogFields(s.ctx, []tyber.Detail{})...) + s.Logger().Debug("Got device chain key", tyber.FormatStepLogFields(s.ctx, []tyber.Detail{})...) metaImpl, err := s.groupMetadataStore(ctx, g, options) if err != nil { @@ -464,7 +440,7 @@ func (s *WeshOrbitDB) OpenGroup(ctx context.Context, g *protocoltypes.Group, opt s.Logger().Debug("Got message store", tyber.FormatStepLogFields(s.ctx, []tyber.Detail{})...) - gc := NewContextGroup(g, metaImpl, messagesImpl, s.messageKeystore, memberDevice, s.Logger()) + gc := NewContextGroup(g, metaImpl, messagesImpl, s.secretStore, memberDevice, s.Logger()) s.Logger().Debug("Created group context", tyber.FormatStepLogFields(s.ctx, []tyber.Detail{})...) @@ -562,7 +538,7 @@ func (s *WeshOrbitDB) storeForGroup(ctx context.Context, o iface.BaseOrbitDB, g s.messageMarshaler.RegisterGroup(addr.String(), g) - linkKey, err := cryptoutil.GetLinkKeyArray(g) + linkKey, err := g.GetLinkKeyArray() if err != nil { return nil, err } diff --git a/orbitdb_many_adds_berty_test.go b/orbitdb_many_adds_berty_test.go index 1faa1607..1fae5989 100644 --- a/orbitdb_many_adds_berty_test.go +++ b/orbitdb_many_adds_berty_test.go @@ -18,6 +18,7 @@ import ( "berty.tech/go-orbit-db/iface" "berty.tech/weshnet/pkg/ipfsutil" "berty.tech/weshnet/pkg/protocoltypes" + "berty.tech/weshnet/pkg/secretstore" "berty.tech/weshnet/pkg/testutil" ) @@ -42,8 +43,13 @@ func testAddBerty(ctx context.Context, t *testing.T, node ipfsutil.CoreAPIMock, baseDS = sync_ds.MutexWrap(baseDS) defer testutil.Close(t, baseDS) + secretStore, err := secretstore.NewSecretStore(baseDS, nil) + require.NoError(t, err) + defer secretStore.Close() + odb, err := NewWeshOrbitDB(ctx, api, &NewOrbitDBOptions{ - Datastore: baseDS, + Datastore: baseDS, + SecretStore: secretStore, }) require.NoError(t, err) diff --git a/orbitdb_utils_test.go b/orbitdb_utils_test.go index aace53ad..6ca1f82c 100644 --- a/orbitdb_utils_test.go +++ b/orbitdb_utils_test.go @@ -102,7 +102,7 @@ func waitForBertyEventType(ctx context.Context, t *testing.T, ms *MetadataStore, handledEvents[eID] = struct{}{} - e := &protocoltypes.GroupAddDeviceSecret{} + e := &protocoltypes.GroupAddDeviceChainKey{} if err := e.Unmarshal(evt.Event); err != nil { t.Fatalf(" err: %+v\n", err.Error()) } diff --git a/pkg/bertypush/push_handler.go b/pkg/bertypush/push_handler.go deleted file mode 100644 index 2bc9b250..00000000 --- a/pkg/bertypush/push_handler.go +++ /dev/null @@ -1,290 +0,0 @@ -package bertypush - -import ( - "context" - "fmt" - "time" - - ds "github.com/ipfs/go-datastore" - ds_sync "github.com/ipfs/go-datastore/sync" - "github.com/libp2p/go-libp2p/core/crypto" - "go.uber.org/zap" - "golang.org/x/crypto/curve25519" - "golang.org/x/crypto/nacl/box" - "golang.org/x/crypto/nacl/secretbox" - - "berty.tech/weshnet/internal/datastoreutil" - "berty.tech/weshnet/pkg/cryptoutil" - "berty.tech/weshnet/pkg/errcode" - "berty.tech/weshnet/pkg/protocoltypes" - "berty.tech/weshnet/pkg/pushtypes" -) - -const InMemoryDir = ":memory:" - -type pushHandler struct { - logger *zap.Logger - pushSK *[cryptoutil.KeySize]byte - pushPK *[cryptoutil.KeySize]byte - groupDatastore cryptoutil.GroupDatastoreReadOnly - messageKeystore *cryptoutil.MessageKeystore - accountCache ds.Datastore -} - -func (s *pushHandler) UpdatePushServer(ctx context.Context, server *protocoltypes.PushServer) error { - cachePayload, err := server.Marshal() - if err != nil { - return errcode.ErrSerialization.Wrap(fmt.Errorf("unable to marshal PushServer: %w", err)) - } - - err = s.accountCache.Put(ctx, ds.NewKey(datastoreutil.AccountCacheDatastorePushServerPK), cachePayload) - if err != nil { - return errcode.ErrInternal.Wrap(fmt.Errorf("unable to cache push server info: %s", err)) - } - - return nil -} - -func (s *pushHandler) PushPK() *[cryptoutil.KeySize]byte { - return s.pushPK -} - -func (s *pushHandler) SetPushSK(key *[cryptoutil.KeySize]byte) { - s.pushSK = key - curve25519.ScalarBaseMult(s.pushPK, s.pushSK) -} - -type PushHandler interface { - PushReceive(ctx context.Context, payload []byte) (*protocoltypes.PushReceive_Reply, error) - PushPK() *[cryptoutil.KeySize]byte - UpdatePushServer(ctx context.Context, server *protocoltypes.PushServer) error -} - -var _ PushHandler = (*pushHandler)(nil) - -type PushHandlerOpts struct { - Logger *zap.Logger - PushKey *[cryptoutil.KeySize]byte - DatastoreDir string - RootDatastore ds.Datastore - GroupDatastore *cryptoutil.GroupDatastore - MessageKeystore *cryptoutil.MessageKeystore - AccountCache ds.Datastore -} - -func (opts *PushHandlerOpts) applyPushDefaults() error { - if opts.Logger == nil { - opts.Logger = zap.NewNop() - } - - if opts.RootDatastore == nil { - if opts.DatastoreDir == "" || opts.DatastoreDir == InMemoryDir { - opts.RootDatastore = ds_sync.MutexWrap(ds.NewMapDatastore()) - } else { - opts.RootDatastore = nil - } - } - - if opts.GroupDatastore == nil { - var err error - opts.GroupDatastore, err = cryptoutil.NewGroupDatastore(opts.RootDatastore) - if err != nil { - return err - } - } - - if opts.AccountCache == nil { - opts.AccountCache = datastoreutil.NewNamespacedDatastore(opts.RootDatastore, ds.NewKey(datastoreutil.NamespaceAccountCacheDatastore)) - } - - if opts.MessageKeystore == nil { - opts.MessageKeystore = cryptoutil.NewMessageKeystore(datastoreutil.NewNamespacedDatastore(opts.RootDatastore, ds.NewKey(datastoreutil.NamespaceMessageKeystore)), opts.Logger) - } - - return nil -} - -func NewPushHandler(opts *PushHandlerOpts) (PushHandler, error) { - if opts.PushKey == nil { - return nil, errcode.ErrInvalidInput.Wrap(fmt.Errorf("no cross account push key specified")) - } - - if err := opts.applyPushDefaults(); err != nil { - return nil, err - } - - h := &pushHandler{ - logger: opts.Logger, - pushSK: opts.PushKey, - pushPK: &[cryptoutil.KeySize]byte{}, - groupDatastore: opts.GroupDatastore, - messageKeystore: opts.MessageKeystore, - accountCache: opts.AccountCache, - } - - curve25519.ScalarBaseMult(h.pushPK, h.pushSK) - - return h, nil -} - -func (s *pushHandler) PushReceive(ctx context.Context, payload []byte) (*protocoltypes.PushReceive_Reply, error) { - pushServerPK, err := s.getServerPushPubKey(ctx) - if err != nil { - return nil, errcode.ErrPushUnableToDecrypt.Wrap(err) - } - - oosBytes, err := DecryptPushDataFromServer(payload, pushServerPK, s.pushSK) - if err != nil { - return nil, errcode.ErrPushUnableToDecrypt.Wrap(err) - } - - oosMessageEnv := &pushtypes.OutOfStoreMessageEnvelope{} - if err := oosMessageEnv.Unmarshal(oosBytes); err != nil { - return nil, errcode.ErrDeserialization.Wrap(err) - } - - gPKBytes, err := s.messageKeystore.GetByPushGroupReference(ctx, oosMessageEnv.GroupReference) - if err != nil { - return nil, errcode.ErrNotFound.Wrap(err) - } - - gPK, err := crypto.UnmarshalEd25519PublicKey(gPKBytes) - if err != nil { - return nil, errcode.ErrDeserialization.Wrap(err) - } - - oosMessage, err := DecryptOutOfStoreMessageEnv(ctx, s.groupDatastore, oosMessageEnv, gPK) - if err != nil { - return nil, errcode.ErrCryptoDecrypt.Wrap(err) - } - - clear, newlyDecrypted, err := s.messageKeystore.OpenOutOfStoreMessage(ctx, oosMessage, gPKBytes) - if err != nil { - return nil, errcode.ErrCryptoDecrypt.Wrap(err) - } - - g, err := s.groupDatastore.Get(ctx, gPK) - if err == nil { - if err := s.messageKeystore.UpdatePushGroupReferences(ctx, oosMessage.DevicePK, oosMessage.Counter, g); err != nil { - s.logger.Error("unable to update push group references", zap.Error(err)) - } - } - - return &protocoltypes.PushReceive_Reply{ - Message: oosMessage, - Cleartext: clear, - GroupPublicKey: gPKBytes, - AlreadyReceived: !newlyDecrypted, - }, nil -} - -func DecryptOutOfStoreMessageEnv(ctx context.Context, gd cryptoutil.GroupDatastoreReadOnly, env *pushtypes.OutOfStoreMessageEnvelope, groupPK crypto.PubKey) (*protocoltypes.OutOfStoreMessage, error) { - nonce, err := cryptoutil.NonceSliceToArray(env.Nonce) - if err != nil { - return nil, errcode.ErrInvalidInput.Wrap(err) - } - - g, err := gd.Get(ctx, groupPK) - if err != nil { - return nil, errcode.ErrInvalidInput.Wrap(fmt.Errorf("unable to find group, err: %w", err)) - } - - secret := cryptoutil.GetSharedSecret(g) - - data, ok := secretbox.Open(nil, env.Box, nonce, secret) - if !ok { - return nil, errcode.ErrCryptoDecrypt - } - - outOfStoreMessage := &protocoltypes.OutOfStoreMessage{} - if err := outOfStoreMessage.Unmarshal(data); err != nil { - return nil, errcode.ErrDeserialization.Wrap(err) - } - - return outOfStoreMessage, nil -} - -func (s *pushHandler) getServerPushPubKey(ctx context.Context) (*[cryptoutil.KeySize]byte, error) { - serverBytes, err := s.accountCache.Get(ctx, ds.NewKey(datastoreutil.AccountCacheDatastorePushServerPK)) - if err != nil { - return nil, errcode.ErrInternal.Wrap(fmt.Errorf("missing push server data: %w", err)) - } - - if len(serverBytes) == 0 { - return nil, errcode.ErrInternal.Wrap(fmt.Errorf("got an empty push server data")) - } - - server := &protocoltypes.PushServer{} - if err := server.Unmarshal(serverBytes); err != nil { - return nil, errcode.ErrDeserialization.Wrap(fmt.Errorf("unable to deserialize push server data: %w", err)) - } - - if l := len(server.ServerKey); l != cryptoutil.KeySize { - return nil, errcode.ErrInvalidInput.Wrap(fmt.Errorf("invalid push pk size, expected %d bytes, got %d", cryptoutil.KeySize, l)) - } - - out := [cryptoutil.KeySize]byte{} - copy(out[:], server.ServerKey) - - return &out, nil -} - -type pushHandlerClient struct { - serviceClient protocoltypes.ProtocolServiceClient -} - -func (p *pushHandlerClient) PushReceive(ctx context.Context, payload []byte) (*protocoltypes.PushReceive_Reply, error) { - ctx, cancel := context.WithTimeout(ctx, time.Second*5) - defer cancel() - - return p.serviceClient.PushReceive(ctx, &protocoltypes.PushReceive_Request{Payload: payload}) -} - -func (p *pushHandlerClient) PushPK() *[32]byte { - // TODO: not supported in client mode - return nil -} - -func (p *pushHandlerClient) SetPushSK(i *[32]byte) { - // TODO: not supported in client mode -} - -func (p *pushHandlerClient) UpdatePushServer(ctx context.Context, server *protocoltypes.PushServer) error { - ctx, cancel := context.WithTimeout(ctx, time.Second*5) - defer cancel() - - _, err := p.serviceClient.PushSetServer(ctx, &protocoltypes.PushSetServer_Request{Server: server}) - - return err -} - -func NewPushHandlerViaProtocol(ctx context.Context, serviceClient protocoltypes.ProtocolServiceClient) PushHandler { - return &pushHandlerClient{serviceClient: serviceClient} -} - -func DecryptPushDataFromServer(data []byte, serverPK, ownSK *[32]byte) ([]byte, error) { - if serverPK == nil { - return nil, errcode.ErrPushUnableToDecrypt.Wrap(fmt.Errorf("no push server public key provided")) - } - - if ownSK == nil { - return nil, errcode.ErrPushUnableToDecrypt.Wrap(fmt.Errorf("no push receiver secret key provided")) - } - - pushEnv := &pushtypes.PushExposedData{} - if err := pushEnv.Unmarshal(data); err != nil { - return nil, errcode.ErrPushInvalidPayload.Wrap(err) - } - - nonce, err := cryptoutil.NonceSliceToArray(pushEnv.Nonce) - if err != nil { - return nil, errcode.ErrPushInvalidPayload.Wrap(err) - } - - msgBytes, ok := box.Open(nil, pushEnv.Box, nonce, serverPK, ownSK) - if !ok { - return nil, errcode.ErrPushUnableToDecrypt.Wrap(fmt.Errorf("box.Open failed")) - } - - return msgBytes, nil -} diff --git a/pkg/cryptoutil/group.go b/pkg/cryptoutil/group.go deleted file mode 100644 index 5cbcc4b1..00000000 --- a/pkg/cryptoutil/group.go +++ /dev/null @@ -1,85 +0,0 @@ -package cryptoutil - -import ( - "encoding/binary" - "fmt" - "io" - - "golang.org/x/crypto/hkdf" - "golang.org/x/crypto/sha3" - - "berty.tech/weshnet/pkg/errcode" -) - -const PushSecretNamespace = "push_secret_ref" // nolint:gosec - -type GroupWithSecret interface { - GetPublicKey() []byte - GetSecret() []byte -} - -type GroupWithLinkKey interface { - GroupWithSecret - GetLinkKey() []byte -} - -func ComputeLinkKey(publicKey, secret []byte) (*[KeySize]byte, error) { - arr := [KeySize]byte{} - - kdf := hkdf.New(sha3.New256, secret, nil, publicKey) - if _, err := io.ReadFull(kdf, arr[:]); err != nil { - return nil, errcode.ErrStreamRead.Wrap(err) - } - - return &arr, nil -} - -func GetLinkKeyArray(m GroupWithLinkKey) (*[KeySize]byte, error) { - if len(m.GetLinkKey()) == KeySize { - arr := [KeySize]byte{} - - for i, c := range m.GetLinkKey() { - arr[i] = c - } - - return &arr, nil - } - - return ComputeLinkKey(m.GetPublicKey(), m.GetSecret()) -} - -func GetSharedSecret(m GroupWithLinkKey) *[KeySize]byte { - sharedSecret := [KeySize]byte{} - copy(sharedSecret[:], m.GetSecret()) - - return &sharedSecret -} - -func GetGroupPushSecret(m GroupWithSecret) ([]byte, error) { - if len(m.GetSecret()) == 0 { - return nil, errcode.ErrInvalidInput.Wrap(fmt.Errorf("no secret known for group")) - } - - arr := [KeySize]byte{} - - kdf := hkdf.New(sha3.New256, m.GetSecret(), nil, []byte(PushSecretNamespace)) - if _, err := io.ReadFull(kdf, arr[:]); err != nil { - return nil, errcode.ErrStreamRead.Wrap(err) - } - - return arr[:], nil -} - -func CreatePushGroupReference(sender []byte, counter uint64, secret []byte) ([]byte, error) { - arr := [KeySize]byte{} - - buf := make([]byte, 8) - binary.BigEndian.PutUint64(buf, counter) - - kdf := hkdf.New(sha3.New256, secret, nil, append(sender, buf...)) - if _, err := io.ReadFull(kdf, arr[:]); err != nil { - return nil, errcode.ErrStreamRead.Wrap(err) - } - - return arr[:], nil -} diff --git a/pkg/cryptoutil/group_store.go b/pkg/cryptoutil/group_store.go deleted file mode 100644 index 202fe1a0..00000000 --- a/pkg/cryptoutil/group_store.go +++ /dev/null @@ -1,213 +0,0 @@ -package cryptoutil - -import ( - "context" - "crypto/ed25519" - "crypto/sha256" - "encoding/base64" - "fmt" - "io" - "io/ioutil" - - "github.com/ipfs/go-datastore" - "github.com/libp2p/go-libp2p/core/crypto" - "golang.org/x/crypto/hkdf" - - "berty.tech/weshnet/internal/datastoreutil" - "berty.tech/weshnet/pkg/errcode" - "berty.tech/weshnet/pkg/protocoltypes" -) - -const NamespaceGroupDatastore = "account_groups_datastore" - -type GroupDatastore struct { - store datastore.Datastore -} - -type GroupDatastoreReadOnly interface { - Has(ctx context.Context, key crypto.PubKey) (bool, error) - Get(ctx context.Context, key crypto.PubKey) (*protocoltypes.Group, error) -} - -func (gd *GroupDatastore) key(key []byte) datastore.Key { - return datastore.NewKey(base64.RawURLEncoding.EncodeToString(key)) -} - -func (gd *GroupDatastore) Has(ctx context.Context, key crypto.PubKey) (bool, error) { - keyBytes, err := key.Raw() - if err != nil { - return false, errcode.ErrSerialization.Wrap(err) - } - - return gd.store.Has(ctx, gd.key(keyBytes)) -} - -func (gd *GroupDatastore) Get(ctx context.Context, key crypto.PubKey) (*protocoltypes.Group, error) { - keyBytes, err := key.Raw() - if err != nil { - return nil, errcode.ErrSerialization.Wrap(err) - } - - data, err := gd.store.Get(ctx, gd.key(keyBytes)) - if err != nil { - return nil, errcode.ErrMissingMapKey.Wrap(err) - } - - g := &protocoltypes.Group{} - if err := g.Unmarshal(data); err != nil { - return nil, errcode.ErrDeserialization.Wrap(err) - } - - return g, nil -} - -func (gd *GroupDatastore) Put(ctx context.Context, g *protocoltypes.Group) error { - pk, err := g.GetPubKey() - if err != nil { - return errcode.ErrInvalidInput.Wrap(err) - } - - if ok, err := gd.Has(ctx, pk); err != nil { - return errcode.ErrInvalidInput.Wrap(err) - } else if ok { - return nil - } - - data, err := g.Marshal() - if err != nil { - return errcode.ErrSerialization.Wrap(err) - } - - if err := gd.store.Put(ctx, gd.key(g.PublicKey), data); err != nil { - return errcode.ErrKeystorePut.Wrap(err) - } - - return nil -} - -func (gd *GroupDatastore) Delete(ctx context.Context, pk crypto.PubKey) error { - pkBytes, err := pk.Raw() - if err != nil { - return errcode.ErrSerialization.Wrap(err) - } - - return gd.store.Delete(ctx, gd.key(pkBytes)) -} - -func (gd *GroupDatastore) PutForContactPK(ctx context.Context, pk crypto.PubKey, deviceKeystore DeviceKeystore) error { - if deviceKeystore == nil { - return errcode.ErrInvalidInput.Wrap(fmt.Errorf("missing device keystore")) - } - - sk, err := deviceKeystore.ContactGroupPrivKey(pk) - if err != nil { - return errcode.ErrCryptoKeyGeneration.Wrap(err) - } - - g, err := GetGroupForContact(sk) - if err != nil { - return errcode.ErrOrbitDBOpen.Wrap(err) - } - - if err := gd.Put(ctx, g); err != nil { - return errcode.ErrInternal.Wrap(err) - } - - return nil -} - -func NewGroupDatastore(ds datastore.Datastore) (*GroupDatastore, error) { - if ds == nil { - return nil, errcode.ErrInvalidInput.Wrap(fmt.Errorf("a datastore is expected")) - } - - return &GroupDatastore{ - store: datastoreutil.NewNamespacedDatastore(ds, datastore.NewKey(NamespaceGroupDatastore)), - }, nil -} - -func GetGroupForContact(contactPairSK crypto.PrivKey) (*protocoltypes.Group, error) { - groupSK, groupSecretSK, err := GetKeysForGroupOfContact(contactPairSK) - if err != nil { - return nil, errcode.ErrCryptoKeyGeneration.Wrap(err) - } - pubBytes, err := groupSK.GetPublic().Raw() - if err != nil { - return nil, errcode.ErrSerialization.Wrap(err) - } - - signingBytes, err := SeedFromEd25519PrivateKey(groupSecretSK) - if err != nil { - return nil, errcode.ErrSerialization.Wrap(err) - } - - return &protocoltypes.Group{ - PublicKey: pubBytes, - Secret: signingBytes, - SecretSig: nil, - GroupType: protocoltypes.GroupTypeContact, - }, nil -} - -func GetGroupForAccount(priv, signing crypto.PrivKey) (*protocoltypes.Group, error) { - pubBytes, err := priv.GetPublic().Raw() - if err != nil { - return nil, errcode.ErrSerialization.Wrap(err) - } - - signingBytes, err := SeedFromEd25519PrivateKey(signing) - if err != nil { - return nil, errcode.ErrSerialization.Wrap(err) - } - - return &protocoltypes.Group{ - PublicKey: pubBytes, - Secret: signingBytes, - SecretSig: nil, - GroupType: protocoltypes.GroupTypeAccount, - }, nil -} - -func GetKeysForGroupOfContact(contactPairSK crypto.PrivKey) (crypto.PrivKey, crypto.PrivKey, error) { - // Salt length must be equal to hash length (64 bytes for sha256) - hash := sha256.New - - ck, err := contactPairSK.Raw() - if err != nil { - return nil, nil, errcode.ErrSerialization.Wrap(err) - } - - // Generate Pseudo Random Key using ck as IKM and salt - prk := hkdf.Extract(hash, ck, nil) - if len(prk) == 0 { - return nil, nil, errcode.ErrInternal - } - - // Expand using extracted prk and groupID as info (kind of namespace) - kdf := hkdf.Expand(hash, prk, nil) - - // Generate next KDF and message keys - groupSeed, err := ioutil.ReadAll(io.LimitReader(kdf, 32)) - if err != nil { - return nil, nil, errcode.ErrCryptoKeyGeneration.Wrap(err) - } - - groupSecretSeed, err := ioutil.ReadAll(io.LimitReader(kdf, 32)) - if err != nil { - return nil, nil, errcode.ErrCryptoKeyGeneration.Wrap(err) - } - - sk1 := ed25519.NewKeyFromSeed(groupSeed) - groupSK, _, err := crypto.KeyPairFromStdKey(&sk1) - if err != nil { - return nil, nil, errcode.ErrCryptoKeyGeneration.Wrap(err) - } - - sk2 := ed25519.NewKeyFromSeed(groupSecretSeed) - groupSecretSK, _, err := crypto.KeyPairFromStdKey(&sk2) - if err != nil { - return nil, nil, errcode.ErrCryptoKeyGeneration.Wrap(err) - } - - return groupSK, groupSecretSK, nil -} diff --git a/pkg/cryptoutil/keystore_device.go b/pkg/cryptoutil/keystore_device.go deleted file mode 100644 index ec08db79..00000000 --- a/pkg/cryptoutil/keystore_device.go +++ /dev/null @@ -1,327 +0,0 @@ -package cryptoutil - -import ( - "crypto/ed25519" - crand "crypto/rand" - "encoding/hex" - "fmt" - "strings" - "sync" - - "github.com/aead/ecdh" - keystore "github.com/ipfs/go-ipfs-keystore" - "github.com/libp2p/go-libp2p/core/crypto" - "go.uber.org/zap" - - "berty.tech/weshnet/pkg/errcode" - "berty.tech/weshnet/pkg/protocoltypes" -) - -type DeviceKeystore interface { - AccountPrivKey() (crypto.PrivKey, error) - AccountProofPrivKey() (crypto.PrivKey, error) - DevicePrivKey() (crypto.PrivKey, error) - ContactGroupPrivKey(pk crypto.PubKey) (crypto.PrivKey, error) - MemberDeviceForGroup(g *protocoltypes.Group) (*OwnMemberDevice, error) - RestoreAccountKeys(accountKey crypto.PrivKey, accountProofKey crypto.PrivKey) error -} - -type deviceKeystore struct { - ks keystore.Keystore - mu sync.Mutex - logger *zap.Logger -} - -type DeviceKeystoreOpts struct { - Logger *zap.Logger -} - -const ( - keyAccount = "accountSK" - keyAccountProof = "accountProofSK" - keyDevice = "deviceSK" - keyMemberDevice = "memberDeviceSK" - keyMember = "memberSK" - keyContactGroup = "contactGroupSK" -) - -// AccountPrivKey returns the private key associated with the current account -func (a *deviceKeystore) AccountPrivKey() (crypto.PrivKey, error) { - a.mu.Lock() - defer a.mu.Unlock() - - return a.getOrGenerateNamedKey(keyAccount) -} - -// AccountProofPrivKey returns the private key associated with the current account -func (a *deviceKeystore) AccountProofPrivKey() (crypto.PrivKey, error) { - a.mu.Lock() - defer a.mu.Unlock() - - return a.getOrGenerateNamedKey(keyAccountProof) -} - -// DevicePrivKey returns the current Device private key -func (a *deviceKeystore) DevicePrivKey() (crypto.PrivKey, error) { - a.mu.Lock() - defer a.mu.Unlock() - - return a.getOrGenerateNamedKey(keyDevice) -} - -// ContactGroupPrivKey retrieves the deviceKeystore signing key associated with the supplied contact pub key -func (a *deviceKeystore) ContactGroupPrivKey(pk crypto.PubKey) (crypto.PrivKey, error) { - accountSK, err := a.AccountPrivKey() - if err != nil { - return nil, err - } - - return a.getOrComputeECDH(keyContactGroup, pk, accountSK) -} - -// memberDeviceForMultiMemberGroup retrieves the Device signing key associated with the supplied group pub key -func (a *deviceKeystore) memberDeviceForMultiMemberGroup(groupPK crypto.PubKey) (*OwnMemberDevice, error) { - memberSK, err := a.getOrComputeDeviceKeyForGroupMember(groupPK) - if err != nil { - return nil, err - } - - deviceSK, err := a.getOrGenerateDeviceKeyForGroupDevice(groupPK) - if err != nil { - return nil, err - } - - return &OwnMemberDevice{ - member: memberSK, - device: deviceSK, - }, nil -} - -func (a *deviceKeystore) MemberDeviceForGroup(g *protocoltypes.Group) (*OwnMemberDevice, error) { - pk, err := g.GetPubKey() - if err != nil { - return nil, errcode.ErrInvalidInput.Wrap(err) - } - - switch g.GroupType { - case protocoltypes.GroupTypeAccount, protocoltypes.GroupTypeContact: - memberSK, err := a.AccountPrivKey() - if err != nil { - return nil, err - } - - deviceSK, err := a.DevicePrivKey() - if err != nil { - return nil, err - } - - return &OwnMemberDevice{ - member: memberSK, - device: deviceSK, - }, nil - - case protocoltypes.GroupTypeMultiMember: - return a.memberDeviceForMultiMemberGroup(pk) - } - - return nil, errcode.ErrInvalidInput -} - -func (a *deviceKeystore) getOrGenerateNamedKey(name string) (crypto.PrivKey, error) { - sk, err := a.ks.Get(name) - if err == nil { - return sk, nil - } else if err.Error() != keystore.ErrNoSuchKey.Error() { - return nil, err - } - - sk, _, err = crypto.GenerateEd25519Key(crand.Reader) - if err != nil { - return nil, err - } - - if err := a.ks.Put(name, sk); err != nil { - return nil, err - } - - return sk, nil -} - -func (a *deviceKeystore) getOrGenerateDeviceKeyForGroupDevice(pk crypto.PubKey) (crypto.PrivKey, error) { - a.mu.Lock() - defer a.mu.Unlock() - - groupPKRaw, err := pk.Raw() - if err != nil { - return nil, err - } - - name := strings.Join([]string{keyMemberDevice, hex.EncodeToString(groupPKRaw)}, "_") - - return a.getOrGenerateNamedKey(name) -} - -func (a *deviceKeystore) getOrComputeECDH(nameSpace string, pk crypto.PubKey, ownSK crypto.PrivKey) (crypto.PrivKey, error) { - a.mu.Lock() - defer a.mu.Unlock() - - pkRaw, err := pk.Raw() - if err != nil { - return nil, err - } - - name := strings.Join([]string{nameSpace, hex.EncodeToString(pkRaw)}, "_") - - sk, err := a.ks.Get(name) - if err == nil { - return sk, nil - } else if err.Error() != keystore.ErrNoSuchKey.Error() { - return nil, err - } - - skB, pkB, err := EdwardsToMontgomery(ownSK, pk) - if err != nil { - return nil, err - } - - secret := ecdh.X25519().ComputeSecret(skB, pkB) - groupSK := ed25519.NewKeyFromSeed(secret) - - sk, _, err = crypto.KeyPairFromStdKey(&groupSK) - if err != nil { - return nil, err - } - - if err := a.ks.Put(name, sk); err != nil { - return nil, err - } - - return sk, nil -} - -func (a *deviceKeystore) getOrComputeDeviceKeyForGroupMember(pk crypto.PubKey) (crypto.PrivKey, error) { - accountProofSK, err := a.AccountProofPrivKey() - if err != nil { - return nil, err - } - - return a.getOrComputeECDH(keyMember, pk, accountProofSK) -} - -func (a *deviceKeystore) RestoreAccountKeys(sk crypto.PrivKey, proofSK crypto.PrivKey) error { - if sk == nil { - return errcode.ErrInvalidInput.Wrap(fmt.Errorf("missing account key")) - } - - if proofSK == nil { - return errcode.ErrInvalidInput.Wrap(fmt.Errorf("missing account proof key")) - } - - ok, err := a.ks.Has(keyAccount) - if err != nil { - return err - } - - if ok { - return errcode.ErrInvalidInput.Wrap(fmt.Errorf("an account key is already set in this keystore")) - } - - ok, err = a.ks.Has(keyAccountProof) - if err != nil { - return err - } - - if ok { - return errcode.ErrInvalidInput.Wrap(fmt.Errorf("an account proof key is already set in this keystore")) - } - - if err := a.ks.Put(keyAccount, sk); err != nil { - return err - } - - if err := a.ks.Put(keyAccountProof, proofSK); err != nil { - return err - } - - return nil -} - -// OwnMemberDevice is own local Device part of a group -type OwnMemberDevice struct { - member crypto.PrivKey - device crypto.PrivKey -} - -func (d *OwnMemberDevice) PrivateMember() crypto.PrivKey { - return d.member -} - -func (d *OwnMemberDevice) PrivateDevice() crypto.PrivKey { - return d.device -} - -func (d *OwnMemberDevice) Public() *MemberDevice { - return &MemberDevice{ - Member: d.member.GetPublic(), - Device: d.device.GetPublic(), - } -} - -func NewOwnMemberDevice(member crypto.PrivKey, device crypto.PrivKey) *OwnMemberDevice { - return &OwnMemberDevice{ - member: member, - device: device, - } -} - -// MemberDevice is a remote Device part of a group -type MemberDevice struct { - Member crypto.PubKey - Device crypto.PubKey -} - -func NewDeviceSecret() (*protocoltypes.DeviceSecret, error) { - chainKey := make([]byte, 32) - _, err := crand.Read(chainKey) - if err != nil { - return nil, errcode.ErrCryptoRandomGeneration.Wrap(err) - } - - return &protocoltypes.DeviceSecret{ - ChainKey: chainKey, - Counter: 0, - }, nil -} - -// NewDeviceKeystore creates a new deviceKeystore instance, if the keystore does not hold an deviceKeystore key, one will be created when required -func NewDeviceKeystore(ks keystore.Keystore, opts *DeviceKeystoreOpts) DeviceKeystore { - if opts == nil { - opts = &DeviceKeystoreOpts{} - } - - if opts.Logger == nil { - opts.Logger = zap.NewNop() - } - - return &deviceKeystore{ - ks: ks, - logger: opts.Logger, - } -} - -// NewWithExistingKeys creates a new deviceKeystore instance and registers the supplied secret key, useful when migrating deviceKeystore to another device -func NewWithExistingKeys(ks keystore.Keystore, sk crypto.PrivKey, proofSK crypto.PrivKey) (DeviceKeystore, error) { - acc := &deviceKeystore{ - ks: ks, - } - - if err := ks.Put(keyAccount, sk); err != nil { - return nil, err - } - - if err := ks.Put(keyAccountProof, proofSK); err != nil { - return nil, err - } - - return acc, nil -} diff --git a/pkg/cryptoutil/keystore_device_test.go b/pkg/cryptoutil/keystore_device_test.go deleted file mode 100644 index b906581a..00000000 --- a/pkg/cryptoutil/keystore_device_test.go +++ /dev/null @@ -1,175 +0,0 @@ -package cryptoutil_test - -import ( - "testing" - - keystore "github.com/ipfs/go-ipfs-keystore" - "github.com/stretchr/testify/assert" - - "berty.tech/weshnet" - "berty.tech/weshnet/pkg/cryptoutil" -) - -func Test_New_AccountPrivKey_AccountProofPrivKey(t *testing.T) { - ks := keystore.NewMemKeystore() - acc := cryptoutil.NewDeviceKeystore(ks, nil) - assert.NotNil(t, acc) - - sk1, err := acc.AccountPrivKey() - assert.NoError(t, err) - assert.NotNil(t, sk1) - - sk2, err := acc.AccountPrivKey() - assert.NoError(t, err) - assert.NotNil(t, sk2) - - assert.True(t, sk1.Equals(sk2)) - - skProof1, err := acc.AccountProofPrivKey() - assert.NoError(t, err) - assert.NotNil(t, skProof1) - - skProof2, err := acc.AccountProofPrivKey() - assert.NoError(t, err) - assert.NotNil(t, skProof2) - - assert.True(t, skProof1.Equals(skProof2)) - assert.False(t, sk1.Equals(skProof1)) - assert.False(t, sk1.Equals(skProof2)) - assert.False(t, sk2.Equals(skProof1)) - assert.False(t, sk2.Equals(skProof2)) -} - -func Test_NewWithExistingKeys_AccountPrivKey_AccountProofPrivKey(t *testing.T) { - ks1 := keystore.NewMemKeystore() - acc1 := cryptoutil.NewDeviceKeystore(ks1, nil) - - sk1, err := acc1.AccountPrivKey() - assert.NoError(t, err) - assert.NotNil(t, sk1) - - skProof1, err := acc1.AccountProofPrivKey() - assert.NoError(t, err) - assert.NotNil(t, skProof1) - - ks2 := keystore.NewMemKeystore() - acc2, err := cryptoutil.NewWithExistingKeys(ks2, sk1, skProof1) - assert.NoError(t, err) - assert.NotNil(t, acc2) - - sk2, err := acc2.AccountPrivKey() - assert.NoError(t, err) - assert.NotNil(t, sk2) - - assert.True(t, sk1.Equals(sk2)) - - skProof2, err := acc2.AccountProofPrivKey() - assert.NoError(t, err) - assert.NotNil(t, skProof2) - - assert.True(t, skProof1.Equals(skProof2)) - assert.False(t, sk1.Equals(skProof1)) - assert.False(t, sk1.Equals(skProof2)) - assert.False(t, sk2.Equals(skProof1)) - assert.False(t, sk2.Equals(skProof2)) -} - -func Test_DevicePrivKey(t *testing.T) { - ks1 := keystore.NewMemKeystore() - acc1 := cryptoutil.NewDeviceKeystore(ks1, nil) - - sk1, err := acc1.AccountPrivKey() - assert.NoError(t, err) - assert.NotNil(t, sk1) - - skProof1, err := acc1.AccountProofPrivKey() - assert.NoError(t, err) - assert.NotNil(t, skProof1) - - ks2 := keystore.NewMemKeystore() - acc2, err := cryptoutil.NewWithExistingKeys(ks2, sk1, skProof1) - assert.NoError(t, err) - assert.NotNil(t, acc2) - - dev1, err := acc1.DevicePrivKey() - assert.NoError(t, err) - assert.NotNil(t, dev1) - - dev2, err := acc2.DevicePrivKey() - assert.NoError(t, err) - assert.NotNil(t, dev2) - - assert.False(t, dev1.Equals(dev2)) -} - -func Test_ContactGroupPrivKey(t *testing.T) { - ks1 := keystore.NewMemKeystore() - acc1 := cryptoutil.NewDeviceKeystore(ks1, nil) - - sk1, err := acc1.AccountPrivKey() - assert.NoError(t, err) - assert.NotNil(t, sk1) - - ks2 := keystore.NewMemKeystore() - acc2 := cryptoutil.NewDeviceKeystore(ks2, nil) - - sk2, err := acc2.AccountPrivKey() - assert.NoError(t, err) - assert.NotNil(t, sk2) - - skGrp1, err := acc1.ContactGroupPrivKey(sk2.GetPublic()) - assert.NoError(t, err) - - skGrp2, err := acc2.ContactGroupPrivKey(sk1.GetPublic()) - assert.NoError(t, err) - - assert.True(t, skGrp1.Equals(skGrp2)) -} - -func Test_MemberDeviceForGroup_account(t *testing.T) { -} - -func Test_MemberDeviceForGroup_contact(t *testing.T) { -} - -func Test_MemberDeviceForGroup_multimember(t *testing.T) { - ks1 := keystore.NewMemKeystore() - acc1 := cryptoutil.NewDeviceKeystore(ks1, nil) - - sk1, err := acc1.AccountPrivKey() - assert.NoError(t, err) - assert.NotNil(t, sk1) - - skProof1, err := acc1.AccountProofPrivKey() - assert.NoError(t, err) - assert.NotNil(t, skProof1) - - ks2 := keystore.NewMemKeystore() - acc2, err := cryptoutil.NewWithExistingKeys(ks2, sk1, skProof1) - assert.NoError(t, err) - assert.NotNil(t, acc2) - - g, _, err := weshnet.NewGroupMultiMember() - assert.NoError(t, err) - - omd1, err := acc1.MemberDeviceForGroup(g) - assert.NoError(t, err) - - omd2, err := acc2.MemberDeviceForGroup(g) - assert.NoError(t, err) - - omd1MB, err := omd1.PrivateMember().Raw() - assert.NoError(t, err) - - omd2MB, err := omd2.PrivateMember().Raw() - assert.NoError(t, err) - - omd1DB, err := omd1.PrivateDevice().Raw() - assert.NoError(t, err) - - omd2DB, err := omd2.PrivateDevice().Raw() - assert.NoError(t, err) - - assert.Equal(t, omd1MB, omd2MB) - assert.NotEqual(t, omd1DB, omd2DB) -} diff --git a/pkg/cryptoutil/keystore_message.go b/pkg/cryptoutil/keystore_message.go deleted file mode 100644 index 5526fc6c..00000000 --- a/pkg/cryptoutil/keystore_message.go +++ /dev/null @@ -1,918 +0,0 @@ -package cryptoutil - -import ( - "context" - "encoding/base64" - "fmt" - "sync" - - "github.com/ipfs/go-cid" - "github.com/ipfs/go-datastore" - dssync "github.com/ipfs/go-datastore/sync" - "github.com/libp2p/go-libp2p/core/crypto" - "go.uber.org/zap" - "golang.org/x/crypto/nacl/secretbox" - - "berty.tech/weshnet/pkg/errcode" - "berty.tech/weshnet/pkg/logutil" - "berty.tech/weshnet/pkg/protocoltypes" -) - -const precomputePushRefsCount = 100 - -// MessageKeystore is a key-value store for storing values related to message -// opening. It has the following namespaces: -// - `chainKeyForDeviceOnGroup`: -// Storing the current state of a device chain key for a given group. -// It contains the secret used to derive the next value of the chain key -// and used to generate a message key for the message at `counter` value, -// then put in the `precomputedMessageKeys` namespace. -// - `precomputedMessageKeys`: -// Storing precomputed message keys for a given group, device and message -// counter. As the chain key stored has already been derived, these -// message keys need to be computed beforehand. The corresponding message -// can then be decrypted via a quick lookup. -// - `messageKeyForCIDs`: -// Containing the message key for a given message CID once the -// corresponding message has been decrypted. -// - `outOfStoreGroupHint`: -// Keys are a HMAC value associated to a group public key. It is used when -// receiving an out-of-store message (e.g. a push notification) to -// identify the group on which the message belongs, which can then -// be decrypted. -type MessageKeystore struct { - lock sync.Mutex - preComputedKeysCount int - store *dssync.MutexDatastore - logger *zap.Logger -} - -type DecryptInfo struct { - NewlyDecrypted bool - MK *[32]byte - Cid cid.Cid -} - -// GetDeviceChainKey returns the device secret for the given group and device. -func (m *MessageKeystore) GetDeviceChainKey(ctx context.Context, groupPK, pk crypto.PubKey) (*protocoltypes.DeviceSecret, error) { - if m == nil { - return nil, errcode.ErrInvalidInput - } - - pkB, err := pk.Raw() - if err != nil { - return nil, errcode.ErrSerialization.Wrap(err) - } - - groupRaw, err := groupPK.Raw() - if err != nil { - return nil, errcode.ErrSerialization.Wrap(err) - } - - key := idForCurrentCK(groupRaw, pkB) - - dsBytes, err := m.store.Get(ctx, key) - if err == datastore.ErrNotFound { - return nil, errcode.ErrMissingInput.Wrap(err) - } else if err != nil { - return nil, errcode.ErrMessageKeyPersistenceGet.Wrap(err) - } - - ds := &protocoltypes.DeviceSecret{} - if err := ds.Unmarshal(dsBytes); err != nil { - return nil, errcode.ErrInvalidInput - } - - return ds, nil -} - -// HasSecretForRawDevicePK returns true if the device secret is known for the given group and device. -func (m *MessageKeystore) HasSecretForRawDevicePK(ctx context.Context, groupPK, devicePK []byte) (has bool) { - if m == nil { - return false - } - - key := idForCurrentCK(groupPK, devicePK) - has, _ = m.store.Has(ctx, key) - return -} - -// delPrecomputedKey deletes the message key in the cache namespace for the given group, device and counter. -func (m *MessageKeystore) delPrecomputedKey(ctx context.Context, groupPK, device crypto.PubKey, counter uint64) error { - if m == nil { - return errcode.ErrInvalidInput - } - - deviceRaw, err := device.Raw() - if err != nil { - return errcode.ErrSerialization.Wrap(err) - } - - groupRaw, err := groupPK.Raw() - if err != nil { - return errcode.ErrSerialization.Wrap(err) - } - - id := idForPrecomputeMK(groupRaw, deviceRaw, counter) - if err := m.store.Delete(ctx, id); err != nil { - return errcode.ErrMessageKeyPersistencePut.Wrap(err) - } - - return nil -} - -// PostDecryptActions is called after a message has been decrypted. -// It saves the message key from the cache namespace to find it quickly on subsequent read operations. -// It derives the chain key in the cache namespace. -func (m *MessageKeystore) PostDecryptActions(ctx context.Context, di *DecryptInfo, g *protocoltypes.Group, ownPK crypto.PubKey, headers *protocoltypes.MessageHeaders) error { - if m == nil { - return errcode.ErrInvalidInput - } - - // Message was newly decrypted, we can save the message key and derive - // future keys if necessary. - if di == nil || !di.NewlyDecrypted { - return nil - } - - var ( - ds *protocoltypes.DeviceSecret - err error - ) - - pk, err := crypto.UnmarshalEd25519PublicKey(headers.DevicePK) - if err != nil { - return errcode.ErrDeserialization.Wrap(err) - } - - groupPK, err := g.GetPubKey() - if err != nil { - return errcode.ErrDeserialization.Wrap(err) - } - - if err = m.putKeyForCID(ctx, di.Cid, di.MK); err != nil { - return errcode.ErrInternal.Wrap(err) - } - - if err = m.delPrecomputedKey(ctx, groupPK, pk, headers.Counter); err != nil { - return errcode.ErrInternal.Wrap(err) - } - - if ds, err = m.preComputeNextKey(ctx, groupPK, pk); err != nil { - return errcode.ErrInternal.Wrap(err) - } - - // If the message was not emitted by the current Device we might need - // to update the current chain key - if ownPK == nil || !ownPK.Equals(pk) { - if err = m.updateCurrentKey(ctx, groupPK, pk, ds); err != nil { - return errcode.ErrInternal.Wrap(err) - } - } - - return nil -} - -// GetDeviceSecret returns the device secret for the current device on a given group. -// If the chain key has not been created yet, it will be generated and registered. -func (m *MessageKeystore) GetDeviceSecret(ctx context.Context, g *protocoltypes.Group, acc DeviceKeystore) (*protocoltypes.DeviceSecret, error) { - if m == nil { - return nil, errcode.ErrInvalidInput - } - - md, err := acc.MemberDeviceForGroup(g) - if err != nil { - return nil, errcode.ErrInternal.Wrap(err) - } - - groupPK, err := g.GetPubKey() - if err != nil { - return nil, errcode.ErrDeserialization.Wrap(err) - } - - ds, err := m.GetDeviceChainKey(ctx, groupPK, md.device.GetPublic()) - if errcode.Is(err, errcode.ErrMissingInput) { - // If secret does not exist, create it - ds, err := NewDeviceSecret() - if err != nil { - return nil, errcode.ErrCryptoKeyGeneration.Wrap(err) - } - - if err = m.registerChainKey(ctx, g, md.device.GetPublic(), ds, true); err != nil { - return nil, errcode.ErrMessageKeyPersistencePut.Wrap(err) - } - - return ds, nil - } - if err != nil { - return nil, errcode.ErrMessageKeyPersistenceGet.Wrap(err) - } - - return ds, nil -} - -// RegisterChainKey registers a device secret for the given group and device. -// If the device secret is not from the current device, the function will -// precompute and store in the cache namespace the next message keys. -// It is the exported version of registerChainKey. -func (m *MessageKeystore) RegisterChainKey(ctx context.Context, g *protocoltypes.Group, devicePK crypto.PubKey, ds *protocoltypes.DeviceSecret, isOwnPK bool) error { - if m == nil { - return errcode.ErrInvalidInput - } - - return m.registerChainKey(ctx, g, devicePK, ds, isOwnPK) -} - -// registerChainKey registers a device secret for the given group and device. -// If the device secret is not from the current device, the function will -// precompute and store in the cache namespace the next message keys. -func (m *MessageKeystore) registerChainKey(ctx context.Context, g *protocoltypes.Group, devicePK crypto.PubKey, ds *protocoltypes.DeviceSecret, isOwnPK bool) error { - if m == nil { - return errcode.ErrInvalidInput - } - - groupPK, err := g.GetPubKey() - if err != nil { - return errcode.ErrDeserialization.Wrap(err) - } - - if _, err := m.GetDeviceChainKey(ctx, groupPK, devicePK); err == nil { - // Device is already registered, ignore it - m.logger.Debug("device already registered in group", - logutil.PrivateBinary("devicePK", logutil.CryptoKeyToBytes(devicePK)), - logutil.PrivateBinary("groupPK", logutil.CryptoKeyToBytes(groupPK)), - ) - return nil - } - - m.logger.Debug("registering chain key", - logutil.PrivateBinary("devicePK", logutil.CryptoKeyToBytes(devicePK)), - logutil.PrivateBinary("groupPK", logutil.CryptoKeyToBytes(groupPK)), - ) - - // If own Device store key as is, no need to precompute future keys - if isOwnPK { - if err := m.putDeviceChainKey(ctx, groupPK, devicePK, ds); err != nil { - return errcode.ErrInternal.Wrap(err) - } - - return nil - } - - if ds, err = m.preComputeKeys(ctx, devicePK, groupPK, ds); err != nil { - return errcode.ErrCryptoKeyGeneration.Wrap(err) - } - - if err := m.putDeviceChainKey(ctx, groupPK, devicePK, ds); err != nil { - return errcode.ErrInternal.Wrap(err) - } - - devicePKBytes, err := devicePK.Raw() - if err == nil { - if err := m.UpdatePushGroupReferences(ctx, devicePKBytes, ds.Counter, g); err != nil { - m.logger.Error("updating push group references failed", zap.Error(err)) - } - } - - return nil -} - -// preComputeKeys precomputes the next m.preComputedKeysCount keys for the given device and group and put them in the cache namespace. -func (m *MessageKeystore) preComputeKeys(ctx context.Context, device crypto.PubKey, groupPK crypto.PubKey, ds *protocoltypes.DeviceSecret) (*protocoltypes.DeviceSecret, error) { - if m == nil { - return nil, errcode.ErrInvalidInput - } - - ck := ds.ChainKey - counter := ds.Counter - - groupPKBytes, err := groupPK.Raw() - if err != nil { - return nil, errcode.ErrSerialization.Wrap(err) - } - - knownCK, err := m.GetDeviceChainKey(ctx, groupPK, device) - if err != nil && !errcode.Is(err, errcode.ErrMissingInput) { - return nil, errcode.ErrInternal.Wrap(err) - } - - preComputedKeys := []computedKey{} - for i := 0; i < m.GetPrecomputedKeyExpectedCount(); i++ { - counter++ - - knownMK, err := m.getPrecomputedKey(ctx, groupPK, device, counter) - if err != nil && !errcode.Is(err, errcode.ErrMissingInput) { - return nil, errcode.ErrInternal.Wrap(err) - } - - // TODO: Salt? - newCK, mk, err := deriveNextKeys(ck, nil, groupPKBytes) - if err != nil { - return nil, errcode.TODO.Wrap(err) - } - - ck = newCK - - if knownMK != nil && knownCK != nil { - if knownCK.Counter != counter-1 { - continue - } - } - - preComputedKeys = append(preComputedKeys, computedKey{counter, &mk}) - } - - err = m.putPrecomputedKeys(ctx, groupPK, device, preComputedKeys...) - if err != nil { - return nil, errcode.TODO.Wrap(err) - } - - return &protocoltypes.DeviceSecret{ - Counter: counter, - ChainKey: ck, - }, nil -} - -// preComputeNextKey precomputes the next key for the given group and device and adds it to the cache namespace. -func (m *MessageKeystore) preComputeNextKey(ctx context.Context, groupPK, devicePK crypto.PubKey) (*protocoltypes.DeviceSecret, error) { - if m == nil || devicePK == nil { - return nil, errcode.ErrInvalidInput - } - - groupPKBytes, err := groupPK.Raw() - if err != nil { - return nil, errcode.ErrSerialization.Wrap(err) - } - - ds, err := m.GetDeviceChainKey(ctx, groupPK, devicePK) - if err != nil { - return nil, errcode.ErrInternal.Wrap(err) - } - - newCounter := ds.Counter + 1 - - // TODO: Salt? - newCK, mk, err := deriveNextKeys(ds.ChainKey, nil, groupPKBytes) - if err != nil { - return nil, errcode.TODO.Wrap(err) - } - - err = m.putPrecomputedKeys(ctx, groupPK, devicePK, computedKey{newCounter, &mk}) - if err != nil { - return nil, errcode.TODO.Wrap(err) - } - - return &protocoltypes.DeviceSecret{ - Counter: newCounter, - ChainKey: newCK, - }, nil -} - -// getPrecomputedKey returns the precomputed key put in the cache namespace for the given group and device at the given counter. -func (m *MessageKeystore) getPrecomputedKey(ctx context.Context, groupPK, device crypto.PubKey, counter uint64) (*[32]byte, error) { - if m == nil { - return nil, errcode.ErrInvalidInput - } - - deviceRaw, err := device.Raw() - if err != nil { - return nil, errcode.ErrSerialization.Wrap(err) - } - - groupRaw, err := groupPK.Raw() - if err != nil { - return nil, errcode.ErrSerialization.Wrap(err) - } - - id := idForPrecomputeMK(groupRaw, deviceRaw, counter) - - key, err := m.store.Get(ctx, id) - - if err == datastore.ErrNotFound { - return nil, errcode.ErrMissingInput.Wrap(fmt.Errorf("key for message does not exist in datastore")) - } - if err != nil { - return nil, errcode.ErrMessageKeyPersistenceGet.Wrap(err) - } - - keyArray, err := KeySliceToArray(key) - if err != nil { - return nil, errcode.ErrSerialization - } - - return keyArray, nil -} - -// computedKey is a precomputed message key for a given counter used in the cache namespace. -type computedKey struct { - counter uint64 - mk *[32]byte -} - -// putPrecomputedKeys puts the given precomputed keys in the cache namespace. -// It will try to use a batch if the store supports it. -func (m *MessageKeystore) putPrecomputedKeys(ctx context.Context, groupPK, device crypto.PubKey, preComputedKeys ...computedKey) error { - if m == nil { - return errcode.ErrInvalidInput - } - - m.logger.Debug("putting precomputed keys", zap.Int("count", len(preComputedKeys))) - - if len(preComputedKeys) != 0 { - deviceRaw, err := device.Raw() - if err != nil { - return errcode.ErrSerialization.Wrap(err) - } - - groupRaw, err := groupPK.Raw() - if err != nil { - return errcode.ErrSerialization.Wrap(err) - } - - batch, err := m.store.Batch(ctx) - if err == datastore.ErrBatchUnsupported { - for _, preComputedKey := range preComputedKeys { - id := idForPrecomputeMK(groupRaw, deviceRaw, preComputedKey.counter) - - if err := m.store.Put(ctx, id, preComputedKey.mk[:]); err != nil { - return errcode.ErrMessageKeyPersistencePut.Wrap(err) - } - } - - return nil - } else if err != nil { - return errcode.ErrMessageKeyPersistencePut.Wrap(err) - } - - for _, preComputedKey := range preComputedKeys { - id := idForPrecomputeMK(groupRaw, deviceRaw, preComputedKey.counter) - - if err := batch.Put(ctx, id, preComputedKey.mk[:]); err != nil { - return errcode.ErrMessageKeyPersistencePut.Wrap(err) - } - } - - if err := batch.Commit(ctx); err != nil { - return errcode.ErrMessageKeyPersistencePut.Wrap(err) - } - } - - return nil -} - -// putKeyForCID puts the given message key in the datastore for the a specified CID. -func (m *MessageKeystore) putKeyForCID(ctx context.Context, id cid.Cid, key *[32]byte) error { - if m == nil { - return errcode.ErrInvalidInput - } - - if !id.Defined() { - return nil - } - - err := m.store.Put(ctx, idForMK(id), key[:]) - if err != nil { - return errcode.ErrMessageKeyPersistencePut.Wrap(err) - } - - return nil -} - -// OpenEnvelopePayload opens the payload of a message envelope and returns the decrypted message in its EncryptedMessage form. -// It also performs post decryption actions such as updating message key cache. -func (m *MessageKeystore) OpenEnvelopePayload( - ctx context.Context, - env *protocoltypes.MessageEnvelope, - headers *protocoltypes.MessageHeaders, - g *protocoltypes.Group, - ownPK crypto.PubKey, - id cid.Cid, -) (*protocoltypes.EncryptedMessage, error) { - gPK, err := g.GetPubKey() - if err != nil { - return nil, errcode.ErrDeserialization.Wrap(err) - } - - msgBytes, decryptInfo, err := m.OpenPayload(ctx, id, gPK, env.Message, headers) - if err != nil { - return nil, errcode.ErrCryptoDecryptPayload.Wrap(err) - } - - if err := m.PostDecryptActions(ctx, decryptInfo, g, ownPK, headers); err != nil { - return nil, errcode.TODO.Wrap(err) - } - - var msg protocoltypes.EncryptedMessage - err = msg.Unmarshal(msgBytes) - if err != nil { - return nil, errcode.ErrDeserialization.Wrap(err) - } - - return &msg, nil -} - -// OpenPayload opens the payload of a message envelope and returns the decrypted message. -// It retrieves the message key from the keystore or the cache to decrypt the message. -func (m *MessageKeystore) OpenPayload(ctx context.Context, id cid.Cid, groupPK crypto.PubKey, payload []byte, headers *protocoltypes.MessageHeaders) ([]byte, *DecryptInfo, error) { - if m == nil { - return nil, nil, errcode.ErrInvalidInput - } - - var ( - err error - di = &DecryptInfo{ - Cid: id, - NewlyDecrypted: true, - } - pk crypto.PubKey - ) - - if di.MK, err = m.GetKeyForCID(ctx, id); err == nil { - di.NewlyDecrypted = false - } else { - pk, err = crypto.UnmarshalEd25519PublicKey(headers.DevicePK) - if err != nil { - return nil, nil, errcode.ErrDeserialization.Wrap(err) - } - - di.MK, err = m.getPrecomputedKey(ctx, groupPK, pk, headers.Counter) - if err != nil { - return nil, nil, errcode.ErrCryptoDecrypt.Wrap(err) - } - } - - return m.openPayload(di, pk, payload, headers) -} - -// openPayload opens the payload of a message envelope with the given key and returns the decrypted message with the DecryptInfo struct. -func (m *MessageKeystore) openPayload(di *DecryptInfo, pk crypto.PubKey, payload []byte, headers *protocoltypes.MessageHeaders) ([]byte, *DecryptInfo, error) { - msg, ok := secretbox.Open(nil, payload, uint64AsNonce(headers.Counter), di.MK) - if !ok { - return nil, nil, errcode.ErrCryptoDecrypt.Wrap(fmt.Errorf("secret box failed to open message payload")) - } - - if di.NewlyDecrypted { - if ok, err := pk.Verify(msg, headers.Sig); !ok { - return nil, nil, errcode.ErrCryptoSignatureVerification.Wrap(fmt.Errorf("unable to verify message signature")) - } else if err != nil { - return nil, nil, errcode.ErrCryptoSignatureVerification.Wrap(err) - } - } - - // Message was newly decrypted, we can save the message key and derive - // future keys if necessary. - return msg, di, nil -} - -// GetKeyForCID retrieves the message key for the given message CID. -func (m *MessageKeystore) GetKeyForCID(ctx context.Context, id cid.Cid) (*[32]byte, error) { - if m == nil { - return nil, errcode.ErrInvalidInput - } - - if !id.Defined() { - return nil, errcode.ErrInvalidInput - } - - key, err := m.store.Get(ctx, idForMK(id)) - if err == datastore.ErrNotFound { - return nil, errcode.ErrInvalidInput - } - - keyArray, err := KeySliceToArray(key) - if err != nil { - return nil, errcode.ErrSerialization - } - - return keyArray, nil -} - -// GetPrecomputedKeyExpectedCount returns the number of precomputed keys that should be in the cache namespace of the keystore. -func (m *MessageKeystore) GetPrecomputedKeyExpectedCount() int { - if m == nil { - return 0 - } - - return m.preComputedKeysCount -} - -// putDeviceChainKey stores the given device secret for the given groupPK and devicePK. -func (m *MessageKeystore) putDeviceChainKey(ctx context.Context, groupPK, device crypto.PubKey, ds *protocoltypes.DeviceSecret) error { - if m == nil { - return errcode.ErrInvalidInput - } - - deviceRaw, err := device.Raw() - if err != nil { - return errcode.ErrSerialization.Wrap(err) - } - - groupRaw, err := groupPK.Raw() - if err != nil { - return errcode.ErrSerialization.Wrap(err) - } - - key := idForCurrentCK(groupRaw, deviceRaw) - - data, err := ds.Marshal() - if err != nil { - return errcode.ErrSerialization.Wrap(err) - } - - err = m.store.Put(ctx, key, data) - if err != nil { - return errcode.ErrMessageKeyPersistencePut.Wrap(err) - } - - return nil -} - -// SealEnvelope encrypts the given payload and returns it as an envelope to be published on the group's store. -// It retrieves the device's chain key from the keystore to encrypt the payload using symmetric encryption. -// The payload is signed using the device's long term private key for the target group. -// It also updates the device secret and stores the next message key in the cache. -func (m *MessageKeystore) SealEnvelope(ctx context.Context, g *protocoltypes.Group, deviceSK crypto.PrivKey, payload []byte) ([]byte, error) { - if m == nil { - return nil, errcode.ErrInvalidInput - } - - if deviceSK == nil || g == nil || m == nil { - return nil, errcode.ErrInvalidInput - } - - groupPK, err := g.GetPubKey() - if err != nil { - return nil, errcode.ErrDeserialization.Wrap(err) - } - - m.lock.Lock() - defer m.lock.Unlock() - - ds, err := m.GetDeviceChainKey(ctx, groupPK, deviceSK.GetPublic()) - if err != nil { - return nil, errcode.ErrInternal.Wrap(fmt.Errorf("unable to get device chainkey: %w", err)) - } - - env, err := SealEnvelope(payload, ds, deviceSK, g) - if err != nil { - return nil, errcode.ErrCryptoEncrypt.Wrap(fmt.Errorf("unable to seal envelope: %w", err)) - } - - if err := m.DeriveDeviceSecret(ctx, g, deviceSK.GetPublic()); err != nil { - return nil, errcode.ErrCryptoKeyGeneration.Wrap(err) - } - - return env, nil -} - -// DeriveDeviceSecret derives the next device secret from the current device secret and stores it in the cache. -// It also updates the device secret in the keystore. -func (m *MessageKeystore) DeriveDeviceSecret(ctx context.Context, g *protocoltypes.Group, devicePK crypto.PubKey) error { - if m == nil || devicePK == nil { - return errcode.ErrInvalidInput - } - - groupPK, err := g.GetPubKey() - if err != nil { - return errcode.ErrDeserialization.Wrap(err) - } - - ds, err := m.preComputeNextKey(ctx, groupPK, devicePK) - if err != nil { - return errcode.ErrCryptoKeyGeneration.Wrap(err) - } - - if err = m.updateCurrentKey(ctx, groupPK, devicePK, ds); err != nil { - return errcode.ErrCryptoKeyGeneration.Wrap(err) - } - - return nil -} - -// updateCurrentKey updates the current device secret in the keystore if the given device secret has a higher counter. -func (m *MessageKeystore) updateCurrentKey(ctx context.Context, groupPK, pk crypto.PubKey, ds *protocoltypes.DeviceSecret) error { - if m == nil { - return errcode.ErrInvalidInput - } - - currentCK, err := m.GetDeviceChainKey(ctx, groupPK, pk) - if err != nil { - return errcode.ErrInternal.Wrap(err) - } - - if ds.Counter < currentCK.Counter { - return nil - } - - if err = m.putDeviceChainKey(ctx, groupPK, pk, ds); err != nil { - return errcode.ErrInternal.Wrap(err) - } - - return nil -} - -// NewMessageKeystore instantiate a new MessageKeystore -func NewMessageKeystore(s datastore.Datastore, logger *zap.Logger) *MessageKeystore { - if logger == nil { - logger = zap.NewNop() - } - - return &MessageKeystore{ - preComputedKeysCount: 100, - store: dssync.MutexWrap(s), - logger: logger.Named("message-ks"), - } -} - -// nolint:deadcode,unused // NewInMemMessageKeystore instantiate a new MessageKeystore, useful for testing -func NewInMemMessageKeystore(logger *zap.Logger) (*MessageKeystore, func()) { - ds := dssync.MutexWrap(datastore.NewMapDatastore()) - - return NewMessageKeystore(ds, logger), func() { _ = ds.Close() } -} - -// OpenOutOfStoreMessage opens the given OutOfStoreMessage and returns the decrypted payload. -// The signature is verified against the given devicePK. -// It derives the next message key and stores it in the cache, but it doesn't update the device secret. -func (m *MessageKeystore) OpenOutOfStoreMessage(ctx context.Context, envelope *protocoltypes.OutOfStoreMessage, groupPublicKey []byte) ([]byte, bool, error) { - if m == nil || envelope == nil || len(groupPublicKey) == 0 { - return nil, false, errcode.ErrInvalidInput - } - - m.lock.Lock() - defer m.lock.Unlock() - - gPK, err := crypto.UnmarshalEd25519PublicKey(groupPublicKey) - if err != nil { - return nil, false, errcode.ErrDeserialization.Wrap(err) - } - - dPK, err := crypto.UnmarshalEd25519PublicKey(envelope.DevicePK) - if err != nil { - return nil, false, errcode.ErrDeserialization.Wrap(err) - } - - _, c, err := cid.CidFromBytes(envelope.CID) - if err != nil { - return nil, false, errcode.ErrDeserialization.Wrap(err) - } - - di := &DecryptInfo{NewlyDecrypted: true} - if di.MK, err = m.GetKeyForCID(ctx, c); err == nil { - di.NewlyDecrypted = false - } else { - di.MK, err = m.getPrecomputedKey(ctx, gPK, dPK, envelope.Counter) - if err != nil { - return nil, false, errcode.ErrCryptoDecrypt.Wrap(err) - } - } - - clear, di, err := m.openPayload(di, dPK, envelope.EncryptedPayload, &protocoltypes.MessageHeaders{ - Counter: envelope.Counter, - DevicePK: envelope.DevicePK, - Sig: envelope.Sig, - }) - if err != nil { - return nil, false, errcode.ErrCryptoDecrypt.Wrap(err) - } - - if ok, err := dPK.Verify(clear, envelope.Sig); !ok { - return nil, false, errcode.ErrCryptoSignatureVerification.Wrap(fmt.Errorf("unable to verify message signature")) - } else if err != nil { - return nil, false, errcode.ErrCryptoSignatureVerification.Wrap(err) - } - - if _, err = m.preComputeNextKey(ctx, gPK, dPK); err != nil { - return nil, false, errcode.ErrInternal.Wrap(err) - } - - return clear, di.NewlyDecrypted, nil -} - -// refKey returns the datastore key of the groupPK for the given push group reference. -func (m *MessageKeystore) refKey(ref []byte) datastore.Key { - return datastore.KeyWithNamespaces([]string{ - "outOfStoreGroupHint", base64.RawURLEncoding.EncodeToString(ref), - }) -} - -// refFirstLastKey returns the datastore key of the FirstLastCounters struct for the given groupPK and devicePK. -func (m *MessageKeystore) refFirstLastKey(groupPK, devicePK []byte) datastore.Key { - return datastore.KeyWithNamespaces([]string{ - "outOfStoreGroupHint", - base64.RawURLEncoding.EncodeToString(groupPK), - base64.RawURLEncoding.EncodeToString(devicePK), - }) -} - -// GetByPushGroupReference returns the groupPK associated with the given push group reference. -func (m *MessageKeystore) GetByPushGroupReference(ctx context.Context, ref []byte) ([]byte, error) { - return m.store.Get(ctx, m.refKey(ref)) -} - -// UpdatePushGroupReferences updates the push group references for the given devicePK and groupPK in the keystore. -// It creates the references for the given range [first + precomputePushRefsCount] and [first - precomputePushRefsCount] and deletes the references out of range. -func (m *MessageKeystore) UpdatePushGroupReferences(ctx context.Context, devicePK []byte, first uint64, group GroupWithSecret) error { - refsExisting := []uint64(nil) - refsToCreate := []uint64(nil) - - groupPushSecret, err := GetGroupPushSecret(group) - if err != nil { - return errcode.ErrCryptoKeyGeneration.Wrap(err) - } - - currentFirst, currentLast, err := m.firstLastCachedGroupRefsForMember(ctx, devicePK, group) - if err == nil { - for i := currentFirst; i != currentLast; i++ { - refsExisting = append(refsExisting, i) - } - } - - // keep previous refs - last := first + precomputePushRefsCount - first -= precomputePushRefsCount - for i := first; i != last; i++ { - found := false - - // Ignore refs that should be kept - for j := 0; j < len(refsExisting); j++ { - if refsExisting[j] == i { - refsExisting[j] = refsExisting[len(refsExisting)-1] - refsExisting = refsExisting[:len(refsExisting)-1] - found = true - break - } - } - - if !found { - refsToCreate = append(refsToCreate, i) - } - } - - // Remove useless old refs - for i := 0; i < len(refsExisting); i++ { - ref, err := CreatePushGroupReference(devicePK, refsExisting[i], groupPushSecret) - if err != nil { - m.logger.Error("creating existing push group reference failed", logutil.PrivateBinary("ref", ref), zap.Error(err)) - continue - } - - if err := m.store.Delete(ctx, m.refKey(ref)); err != nil { - m.logger.Error("deleting existing push group reference failed", logutil.PrivateBinary("ref", ref), zap.Error(err)) - continue - } - } - - // Add new refs - for i := 0; i < len(refsToCreate); i++ { - ref, err := CreatePushGroupReference(devicePK, refsToCreate[i], groupPushSecret) - if err != nil { - m.logger.Error("creating new push group reference failed", logutil.PrivateBinary("ref", ref), zap.Error(err)) - continue - } - - if err := m.store.Put(ctx, m.refKey(ref), group.GetPublicKey()); err != nil { - m.logger.Error("putting new push group reference failed", logutil.PrivateBinary("ref", ref), zap.Error(err)) - continue - } - } - - // Update first/last - if err := m.putFirstLastCachedGroupRefsForMember(ctx, first, last, devicePK, group); err != nil { - m.logger.Error("putting first/last push group reference failed", zap.Error(err)) - } - - return nil -} - -// firstLastCachedGroupRefsForMember returns the first and last cached group references counter for the given devicePK and groupPK. -func (m *MessageKeystore) firstLastCachedGroupRefsForMember(ctx context.Context, devicePK []byte, group GroupWithSecret) (uint64, uint64, error) { - key := m.refFirstLastKey(group.GetPublicKey(), devicePK) - bytes, err := m.store.Get(ctx, key) - if err != nil { - return 0, 0, err - } - - ret := protocoltypes.FirstLastCounters{} - if err := ret.Unmarshal(bytes); err != nil { - return 0, 0, err - } - - return ret.First, ret.Last, nil -} - -// putFirstLastCachedGroupRefsForMember puts the first and last cached group references counter for the given devicePK and groupPK. -func (m *MessageKeystore) putFirstLastCachedGroupRefsForMember(ctx context.Context, first uint64, last uint64, devicePK []byte, group GroupWithSecret) error { - key := m.refFirstLastKey(group.GetPublicKey(), devicePK) - - fistLast := protocoltypes.FirstLastCounters{ - First: first, - Last: last, - } - bytes, err := fistLast.Marshal() - if err != nil { - return err - } - - return m.store.Put(ctx, key, bytes) -} diff --git a/pkg/cryptoutil/keystore_message_utils.go b/pkg/cryptoutil/keystore_message_utils.go deleted file mode 100644 index 1ed4a918..00000000 --- a/pkg/cryptoutil/keystore_message_utils.go +++ /dev/null @@ -1,159 +0,0 @@ -package cryptoutil - -import ( - "crypto/sha256" - "encoding/binary" - "encoding/hex" - "fmt" - "io" - "io/ioutil" - - "github.com/gogo/protobuf/proto" - "github.com/ipfs/go-cid" - "github.com/ipfs/go-datastore" - "github.com/libp2p/go-libp2p/core/crypto" - "golang.org/x/crypto/hkdf" - "golang.org/x/crypto/nacl/secretbox" - - "berty.tech/weshnet/pkg/errcode" - "berty.tech/weshnet/pkg/protocoltypes" -) - -func SealPayload(payload []byte, ds *protocoltypes.DeviceSecret, deviceSK crypto.PrivKey, g *protocoltypes.Group) ([]byte, []byte, error) { - var ( - msgKey [32]byte - err error - ) - - sig, err := deviceSK.Sign(payload) - if err != nil { - return nil, nil, errcode.ErrCryptoSignature.Wrap(err) - } - - if _, msgKey, err = deriveNextKeys(ds.ChainKey, nil, g.GetPublicKey()); err != nil { - return nil, nil, errcode.ErrCryptoKeyGeneration.Wrap(err) - } - - return secretbox.Seal(nil, payload, uint64AsNonce(ds.Counter+1), &msgKey), sig, nil -} - -func SealEnvelope(payload []byte, ds *protocoltypes.DeviceSecret, deviceSK crypto.PrivKey, g *protocoltypes.Group) ([]byte, error) { - encryptedPayload, sig, err := SealPayload(payload, ds, deviceSK, g) - if err != nil { - return nil, errcode.ErrCryptoEncrypt.Wrap(err) - } - - devicePKRaw, err := deviceSK.GetPublic().Raw() - if err != nil { - return nil, errcode.ErrSerialization.Wrap(err) - } - - h := &protocoltypes.MessageHeaders{ - Counter: ds.Counter + 1, - DevicePK: devicePKRaw, - Sig: sig, - } - - headers, err := proto.Marshal(h) - if err != nil { - return nil, errcode.ErrSerialization.Wrap(err) - } - - nonce, err := GenerateNonce() - if err != nil { - return nil, errcode.ErrCryptoNonceGeneration.Wrap(err) - } - - encryptedHeaders := secretbox.Seal(nil, headers, nonce, GetSharedSecret(g)) - - env, err := proto.Marshal(&protocoltypes.MessageEnvelope{ - MessageHeaders: encryptedHeaders, - Message: encryptedPayload, - Nonce: nonce[:], - }) - if err != nil { - return nil, errcode.ErrSerialization.Wrap(err) - } - - return env, nil -} - -func OpenEnvelopeHeaders(data []byte, g *protocoltypes.Group) (*protocoltypes.MessageEnvelope, *protocoltypes.MessageHeaders, error) { - env := &protocoltypes.MessageEnvelope{} - err := env.Unmarshal(data) - if err != nil { - return nil, nil, errcode.ErrDeserialization.Wrap(err) - } - - nonce, err := NonceSliceToArray(env.Nonce) - if err != nil { - return nil, nil, errcode.ErrSerialization.Wrap(err) - } - - headersBytes, ok := secretbox.Open(nil, env.MessageHeaders, nonce, GetSharedSecret(g)) - if !ok { - return nil, nil, errcode.ErrCryptoDecrypt.Wrap(fmt.Errorf("secretbox failed to open headers")) - } - - headers := &protocoltypes.MessageHeaders{} - if err := headers.Unmarshal(headersBytes); err != nil { - return nil, nil, errcode.ErrDeserialization.Wrap(err) - } - - return env, headers, nil -} - -func deriveNextKeys(ck []byte, salt []byte, groupID []byte) ([]byte, [32]byte, error) { - var ( - nextMsg [32]byte - err error - ) - - // Salt length must be equal to hash length (64 bytes for sha256) - hash := sha256.New - - // Generate Pseudo Random Key using ck as IKM and salt - prk := hkdf.Extract(hash, ck, salt) - if len(prk) == 0 { - return nil, nextMsg, errcode.ErrInternal - } - - // Expand using extracted prk and groupID as info (kind of namespace) - kdf := hkdf.Expand(hash, prk, groupID) - - // Generate next KDF and message keys - nextCK, err := ioutil.ReadAll(io.LimitReader(kdf, 32)) - if err != nil { - return nil, nextMsg, errcode.ErrCryptoKeyGeneration.Wrap(err) - } - - nextMsgSlice, err := ioutil.ReadAll(io.LimitReader(kdf, 32)) - if err != nil { - return nil, nextMsg, errcode.ErrCryptoKeyGeneration.Wrap(err) - } - - copy(nextMsg[:], nextMsgSlice) - - return nextCK, nextMsg, nil -} - -func idForPrecomputeMK(groupPK, pk []byte, counter uint64) datastore.Key { - return datastore.KeyWithNamespaces([]string{"precomputedMessageKeys", hex.EncodeToString(groupPK), hex.EncodeToString(pk), fmt.Sprintf("%d", counter)}) -} - -func idForCurrentCK(groupPK, pk []byte) datastore.Key { - return datastore.KeyWithNamespaces([]string{"chainKeyForDeviceOnGroup", hex.EncodeToString(groupPK), hex.EncodeToString(pk)}) -} - -func idForMK(id cid.Cid) datastore.Key { - // TODO: specify the id - return datastore.KeyWithNamespaces([]string{"messageKeyForCIDs", id.String()}) -} - -func uint64AsNonce(val uint64) *[24]byte { - var nonce [24]byte - - binary.BigEndian.PutUint64(nonce[:], val) - - return &nonce -} diff --git a/pkg/cryptoutil/keystore_message_utils_test.go b/pkg/cryptoutil/keystore_message_utils_test.go deleted file mode 100644 index 55bb45ec..00000000 --- a/pkg/cryptoutil/keystore_message_utils_test.go +++ /dev/null @@ -1,616 +0,0 @@ -package cryptoutil_test - -import ( - "context" - "encoding/hex" - "fmt" - "os" - "path" - "testing" - - cid "github.com/ipfs/go-cid" - keystore "github.com/ipfs/go-ipfs-keystore" - "github.com/libp2p/go-libp2p/core/crypto" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "go.uber.org/zap" - - "berty.tech/weshnet" - "berty.tech/weshnet/pkg/cryptoutil" - "berty.tech/weshnet/pkg/protocoltypes" - "berty.tech/weshnet/pkg/testutil" -) - -const precomputePushRefsCount = uint64(100) - -func addDummyMemberInMetadataStore(ctx context.Context, t testing.TB, ms *weshnet.MetadataStore, g *protocoltypes.Group, memberPK crypto.PubKey, join bool) (crypto.PubKey, *protocoltypes.DeviceSecret) { - t.Helper() - - acc := cryptoutil.NewDeviceKeystore(keystore.NewMemKeystore(), nil) - md, err := acc.MemberDeviceForGroup(g) - assert.NoError(t, err) - - ds, err := cryptoutil.NewDeviceSecret() - assert.NoError(t, err) - - if join { - _, err = weshnet.MetadataStoreAddDeviceToGroup(ctx, ms, g, md) - assert.NoError(t, err) - } - - _, err = weshnet.MetadataStoreSendSecret(ctx, ms, g, md, memberPK, ds) - assert.NoError(t, err) - - return md.PrivateDevice().GetPublic(), ds -} - -func mustDeviceSecret(t testing.TB) func(ds *protocoltypes.DeviceSecret, err error) *protocoltypes.DeviceSecret { - return func(ds *protocoltypes.DeviceSecret, err error) *protocoltypes.DeviceSecret { - t.Helper() - - if err != nil { - t.Fatal(err) - } - - return ds - } -} - -func mustMessageHeaders(t testing.TB, sk crypto.PrivKey, counter uint64, payload []byte) *protocoltypes.MessageHeaders { - t.Helper() - - pkB, err := sk.GetPublic().Raw() - if err != nil { - t.Fatal(err) - } - - sig, err := sk.Sign(payload) - if err != nil { - t.Fatal(err) - } - - return &protocoltypes.MessageHeaders{ - Counter: counter, - DevicePK: pkB, - Sig: sig, - } -} - -func Test_EncryptMessagePayload(t *testing.T) { - logger := zap.NewNop() - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - g, gSK, err := weshnet.NewGroupMultiMember() - assert.NoError(t, err) - - _ = gSK - - acc1 := cryptoutil.NewDeviceKeystore(keystore.NewMemKeystore(), nil) - - omd1, err := acc1.MemberDeviceForGroup(g) - assert.NoError(t, err) - - ds1, err := cryptoutil.NewDeviceSecret() - assert.NoError(t, err) - - ds2, err := cryptoutil.NewDeviceSecret() - assert.NoError(t, err) - - initialCounter := ds1.Counter - firstSK := append([]byte(nil), ds1.ChainKey...) - - acc2 := cryptoutil.NewDeviceKeystore(keystore.NewMemKeystore(), nil) - - omd2, err := acc2.MemberDeviceForGroup(g) - - mkh1, cleanup := cryptoutil.NewInMemMessageKeystore(logger) - defer cleanup() - - mkh2, cleanup := cryptoutil.NewInMemMessageKeystore(logger) - defer cleanup() - - gc1 := weshnet.NewContextGroup(g, nil, nil, mkh1, omd1, nil) - gc2 := weshnet.NewContextGroup(g, nil, nil, mkh2, omd2, nil) - - err = mkh1.RegisterChainKey(ctx, g, gc1.DevicePubKey(), ds1, true) - assert.NoError(t, err) - - err = mkh2.RegisterChainKey(ctx, g, gc2.DevicePubKey(), ds2, true) - assert.NoError(t, err) - - payloadRef1 := []byte("ok, this is the first test") - payloadRef2 := []byte("so, this is a second test") - payloadRef3 := []byte("this will be posted many times") - - err = mkh2.RegisterChainKey(ctx, g, omd1.PrivateDevice().GetPublic(), ds1, false) - assert.NoError(t, err) - - gPK, err := g.GetPubKey() - assert.NoError(t, err) - - assert.Equal(t, mustDeviceSecret(t)(mkh1.GetDeviceChainKey(ctx, gPK, omd1.PrivateDevice().GetPublic())).ChainKey, firstSK) - - payloadEnc1, _, err := cryptoutil.SealPayload(payloadRef1, mustDeviceSecret(t)(mkh1.GetDeviceChainKey(ctx, gPK, omd1.PrivateDevice().GetPublic())), omd1.PrivateDevice(), g) - assert.NoError(t, err) - - // secret is derived by SealEnvelope - err = mkh1.DeriveDeviceSecret(ctx, g, omd1.PrivateDevice().GetPublic()) - assert.NoError(t, err) - - assert.NotEqual(t, hex.EncodeToString(payloadRef1), hex.EncodeToString(payloadEnc1)) - - // Messages are encrypted with DeviceSecret.Counter - // uint64 overflows to 0, which is the expected behaviour - - // Test with a wrong counter value - payloadClr1, decryptInfo, err := mkh2.OpenPayload(ctx, cid.Undef, gPK, payloadEnc1, mustMessageHeaders(t, omd1.PrivateDevice(), initialCounter+2, payloadRef1)) - assert.Error(t, err) - assert.Nil(t, decryptInfo) - assert.Equal(t, "", string(payloadClr1)) - - // Test with a valid counter value, but no CID (so no cache) - payloadClr1, decryptInfo, err = mkh2.OpenPayload(ctx, cid.Undef, gPK, payloadEnc1, mustMessageHeaders(t, omd1.PrivateDevice(), initialCounter+1, payloadRef1)) - assert.NoError(t, err) - assert.Equal(t, string(payloadRef1), string(payloadClr1)) - - err = mkh2.PostDecryptActions(ctx, decryptInfo, g, omd2.PrivateDevice().GetPublic(), mustMessageHeaders(t, omd1.PrivateDevice(), initialCounter+1, payloadRef1)) - assert.NoError(t, err) - - ds, err := mkh1.GetDeviceChainKey(ctx, gPK, omd1.PrivateDevice().GetPublic()) - assert.NoError(t, err) - - assert.Equal(t, ds.Counter, initialCounter+1) - assert.NotEqual(t, ds.ChainKey, firstSK) - - payloadEnc2, _, err := cryptoutil.SealPayload(payloadRef1, ds, omd1.PrivateDevice(), g) - assert.NoError(t, err) - - err = mkh1.DeriveDeviceSecret(ctx, g, omd1.PrivateDevice().GetPublic()) - assert.NoError(t, err) - - // Ensure that encrypted message is not the same as the first message - assert.NotEqual(t, hex.EncodeToString(payloadRef1), hex.EncodeToString(payloadEnc2)) - assert.NotEqual(t, hex.EncodeToString(payloadEnc1), hex.EncodeToString(payloadEnc2)) - - payloadClr2, decryptInfo, err := mkh2.OpenPayload(ctx, cid.Undef, gPK, payloadEnc2, mustMessageHeaders(t, omd1.PrivateDevice(), initialCounter+2, payloadRef1)) - assert.NoError(t, err) - - err = mkh2.PostDecryptActions(ctx, decryptInfo, g, omd2.PrivateDevice().GetPublic(), mustMessageHeaders(t, omd1.PrivateDevice(), initialCounter+2, payloadRef1)) - assert.NoError(t, err) - - assert.Equal(t, string(payloadRef1), string(payloadClr2)) - - // Make sure that a message without a CID can't be decrypted twice - payloadClr2, decryptInfo, err = mkh2.OpenPayload(ctx, cid.Undef, gPK, payloadEnc2, mustMessageHeaders(t, omd1.PrivateDevice(), initialCounter+1, payloadRef1)) - assert.Error(t, err) - assert.Equal(t, "", string(payloadClr2)) - - ds, err = mkh1.GetDeviceChainKey(ctx, gPK, omd1.PrivateDevice().GetPublic()) - assert.NoError(t, err) - - // Make sure that a message a CID can be decrypted twice - payloadEnc3, _, err := cryptoutil.SealPayload(payloadRef2, ds, omd1.PrivateDevice(), g) - assert.NoError(t, err) - - err = mkh1.DeriveDeviceSecret(ctx, g, omd1.PrivateDevice().GetPublic()) - assert.NoError(t, err) - - dummyCID1, err := cid.Parse("QmbdQXQh9B2bWZgZJqfbjNPV5jGN2owbQ3vjeYsaDaCDqU") - assert.NoError(t, err) - - dummyCID2, err := cid.Parse("Qmf8oj9wbfu73prNAA1cRQVDqA52gD5B3ApnYQQjcjffH4") - assert.NoError(t, err) - - // Not decrypted message yet, wrong counter value - payloadClr3, decryptInfo, err := mkh2.OpenPayload(ctx, dummyCID1, gPK, payloadEnc3, mustMessageHeaders(t, omd1.PrivateDevice(), initialCounter+2, payloadRef2)) - assert.Error(t, err) - assert.Equal(t, "", string(payloadClr3)) - - payloadClr3, decryptInfo, err = mkh2.OpenPayload(ctx, dummyCID1, gPK, payloadEnc3, mustMessageHeaders(t, omd1.PrivateDevice(), initialCounter+3, payloadRef2)) - assert.NoError(t, err) - assert.Equal(t, string(payloadRef2), string(payloadClr3)) - - err = mkh2.PostDecryptActions(ctx, decryptInfo, g, omd2.PrivateDevice().GetPublic(), mustMessageHeaders(t, omd1.PrivateDevice(), initialCounter+3, payloadRef2)) - assert.NoError(t, err) - - payloadClr3, decryptInfo, err = mkh2.OpenPayload(ctx, dummyCID1, gPK, payloadEnc3, mustMessageHeaders(t, omd1.PrivateDevice(), initialCounter+3, payloadRef2)) - assert.NoError(t, err) - assert.Equal(t, string(payloadRef2), string(payloadClr3)) - - err = mkh2.PostDecryptActions(ctx, decryptInfo, g, omd2.PrivateDevice().GetPublic(), mustMessageHeaders(t, omd1.PrivateDevice(), initialCounter+3, payloadRef2)) - assert.NoError(t, err) - - // Wrong CID - payloadClr3, decryptInfo, err = mkh2.OpenPayload(ctx, dummyCID2, gPK, payloadEnc3, mustMessageHeaders(t, omd1.PrivateDevice(), initialCounter+3, payloadRef2)) - assert.Error(t, err) - assert.Equal(t, "", string(payloadClr3)) - - // Reused CID, wrong counter value - payloadClr3, decryptInfo, err = mkh2.OpenPayload(ctx, dummyCID1, gPK, payloadEnc3, mustMessageHeaders(t, omd1.PrivateDevice(), initialCounter+4, payloadRef2)) - assert.Error(t, err) - assert.Equal(t, "", string(payloadClr3)) - - massExpected := uint64(200) - - // Test appending 200 messages, to ensure new secrets are generated correctly - for i := uint64(0); i < massExpected; i++ { - ds, err = mkh1.GetDeviceChainKey(ctx, gPK, omd1.PrivateDevice().GetPublic()) - assert.NoError(t, err) - - payloadEnc, _, err := cryptoutil.SealPayload(payloadRef3, ds, omd1.PrivateDevice(), g) - assert.NoError(t, err) - - err = mkh1.DeriveDeviceSecret(ctx, g, omd1.PrivateDevice().GetPublic()) - assert.NoError(t, err) - - ds, err = mkh1.GetDeviceChainKey(ctx, gPK, omd1.PrivateDevice().GetPublic()) - assert.NoError(t, err) - - counter := ds.Counter - - payloadClr, decryptInfo, err := mkh2.OpenPayload(ctx, cid.Undef, gPK, payloadEnc, mustMessageHeaders(t, omd1.PrivateDevice(), counter, payloadRef3)) - if !assert.NoError(t, err) { - t.Fatalf("failed at i = %d", i) - } - - err = mkh2.PostDecryptActions(ctx, decryptInfo, g, omd2.PrivateDevice().GetPublic(), mustMessageHeaders(t, omd1.PrivateDevice(), counter, payloadRef3)) - assert.NoError(t, err) - - assert.Equal(t, string(payloadRef3), string(payloadClr)) - } - - ds, err = mkh1.GetDeviceChainKey(ctx, gPK, omd1.PrivateDevice().GetPublic()) - assert.NoError(t, err) - - assert.Equal(t, initialCounter+massExpected+3, ds.Counter) -} - -func Test_EncryptMessageEnvelope(t *testing.T) { - logger := zap.NewNop() - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - g, _, err := weshnet.NewGroupMultiMember() - assert.NoError(t, err) - - acc1 := cryptoutil.NewDeviceKeystore(keystore.NewMemKeystore(), nil) - mkh1, cleanup := cryptoutil.NewInMemMessageKeystore(logger) - defer cleanup() - - omd1, err := acc1.MemberDeviceForGroup(g) - assert.NoError(t, err) - - gc1 := weshnet.NewContextGroup(g, nil, nil, mkh1, omd1, nil) - - ds1, err := weshnet.NewDeviceSecret() - assert.NoError(t, err) - - err = mkh1.RegisterChainKey(ctx, g, gc1.DevicePubKey(), ds1, true) - assert.NoError(t, err) - - acc2 := cryptoutil.NewDeviceKeystore(keystore.NewMemKeystore(), nil) - mkh2, cleanup := cryptoutil.NewInMemMessageKeystore(logger) - defer cleanup() - - omd2, err := acc2.MemberDeviceForGroup(g) - assert.NoError(t, err) - - ds2, err := cryptoutil.NewDeviceSecret() - assert.NoError(t, err) - - payloadRef1, err := (&protocoltypes.EncryptedMessage{Plaintext: []byte("Test payload 1")}).Marshal() - assert.NoError(t, err) - - err = mkh2.RegisterChainKey(ctx, g, omd2.PrivateDevice().GetPublic(), ds2, true) - assert.NoError(t, err) - - err = mkh2.RegisterChainKey(ctx, g, omd1.PrivateDevice().GetPublic(), ds1, false) - assert.NoError(t, err) - - env1, err := cryptoutil.SealEnvelope(payloadRef1, ds1, omd1.PrivateDevice(), g) - assert.NoError(t, err) - - headers, payloadClr1, err := openEnvelope(ctx, t, mkh2, g, omd2.PrivateDevice().GetPublic(), env1, cid.Undef) - assert.NoError(t, err) - - devRaw, err := omd1.PrivateDevice().GetPublic().Raw() - assert.Equal(t, headers.DevicePK, devRaw) - - payloadClrlBytes, err := payloadClr1.Marshal() - assert.NoError(t, err) - assert.Equal(t, payloadRef1, payloadClrlBytes) -} - -func Test_EncryptMessageEnvelopeAndDerive(t *testing.T) { - logger := zap.NewNop() - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - g, _, err := weshnet.NewGroupMultiMember() - assert.NoError(t, err) - - acc1 := cryptoutil.NewDeviceKeystore(keystore.NewMemKeystore(), nil) - acc2 := cryptoutil.NewDeviceKeystore(keystore.NewMemKeystore(), nil) - - omd1, err := acc1.MemberDeviceForGroup(g) - assert.NoError(t, err) - - omd2, err := acc2.MemberDeviceForGroup(g) - assert.NoError(t, err) - - ds1, err := cryptoutil.NewDeviceSecret() - assert.NoError(t, err) - - mkh1, cleanup := cryptoutil.NewInMemMessageKeystore(logger) - defer cleanup() - - mkh2, cleanup := cryptoutil.NewInMemMessageKeystore(logger) - defer cleanup() - - err = mkh1.RegisterChainKey(ctx, g, omd1.PrivateDevice().GetPublic(), ds1, true) - assert.NoError(t, err) - - gPK, err := g.GetPubKey() - assert.NoError(t, err) - - gc1 := weshnet.NewContextGroup(g, nil, nil, mkh1, omd1, nil) - - ds2, err := cryptoutil.NewDeviceSecret() - assert.NoError(t, err) - - err = mkh2.RegisterChainKey(ctx, g, omd2.PrivateDevice().GetPublic(), ds2, true) - assert.NoError(t, err) - - err = mkh2.RegisterChainKey(ctx, g, omd1.PrivateDevice().GetPublic(), ds1, false) - assert.NoError(t, err) - - initialCounter := ds1.Counter - - for i := 0; i < 1000; i++ { - payloadRef, err := (&protocoltypes.EncryptedMessage{Plaintext: []byte("Test payload 1")}).Marshal() - assert.NoError(t, err) - envEncrypted, err := mkh1.SealEnvelope(ctx, g, omd1.PrivateDevice(), payloadRef) - assert.NoError(t, err) - - ds, err := mkh1.GetDeviceChainKey(ctx, gPK, gc1.DevicePubKey()) - if !assert.NoError(t, err) { - t.Fatalf("failed at i = %d", i) - } - assert.Equal(t, ds.Counter, initialCounter+uint64(i+1)) - - headers, payloadClr, err := openEnvelope(ctx, t, mkh2, g, omd2.PrivateDevice().GetPublic(), envEncrypted, cid.Undef) - if !assert.NoError(t, err) { - t.Fatalf("failed at i = %d", i) - } - - if assert.NotNil(t, headers) && assert.NotNil(t, payloadClr) { - devRaw, err := omd1.PrivateDevice().GetPublic().Raw() - assert.NoError(t, err) - - assert.Equal(t, headers.DevicePK, devRaw) - - payloadClrBytes, err := payloadClr.Marshal() - assert.NoError(t, err) - assert.Equal(t, payloadRef, payloadClrBytes) - } else { - break - } - } -} - -func testMessageKeyHolderCatchUp(t *testing.T, expectedNewDevices int, isSlow bool) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - if isSlow { - testutil.FilterSpeed(t, testutil.Slow) - } - - dir := path.Join(os.TempDir(), fmt.Sprintf("%d", os.Getpid()), "MessageKeyHolderCatchUp") - defer os.RemoveAll(dir) - - peers, _, cleanup := weshnet.CreatePeersWithGroupTest(ctx, t, dir, 1, 1) - defer cleanup() - - peer := peers[0] - - mkh1 := peer.MKS - ms1 := peer.GC.MetadataStore() - - devicesPK := make([]crypto.PubKey, expectedNewDevices) - deviceSecrets := make([]*protocoltypes.DeviceSecret, expectedNewDevices) - - for i := 0; i < expectedNewDevices; i++ { - devicesPK[i], deviceSecrets[i] = addDummyMemberInMetadataStore(ctx, t, ms1, peer.GC.Group(), peer.GC.MemberPubKey(), true) - } - - for range peer.GC.FillMessageKeysHolderUsingPreviousData() { - } - - gPK, err := peer.GC.Group().GetPubKey() - require.NoError(t, err) - - for i, dPK := range devicesPK { - ds, err := mkh1.GetDeviceChainKey(ctx, gPK, dPK) - if assert.NoError(t, err) { - assert.Equal(t, deviceSecrets[i].Counter+uint64(mkh1.GetPrecomputedKeyExpectedCount()), ds.Counter) - // Not testing chain key value as we need to derive it from the generated value - } else { - t.Fatalf("failed at iteration %d", i) - } - } -} - -func TestMessageKeyHolderCatchUp(t *testing.T) { - for _, testCase := range []struct { - expectedNewDevices int - slow bool - }{ - { - expectedNewDevices: 2, - slow: false, - }, - { - expectedNewDevices: 10, - slow: true, - }, - } { - testMessageKeyHolderCatchUp(t, testCase.expectedNewDevices, testCase.slow) - } -} - -func testMessageKeyHolderSubscription(t *testing.T, expectedNewDevices int, isSlow bool) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - if isSlow { - testutil.FilterSpeed(t, testutil.Slow) - } - - dir := path.Join(os.TempDir(), fmt.Sprintf("%d", os.Getpid()), "MessageKeyHolderSubscription") - defer os.RemoveAll(dir) - - peers, gSK, cleanup := weshnet.CreatePeersWithGroupTest(ctx, t, dir, 1, 1) - defer cleanup() - - peer := peers[0] - - mkh1 := peer.MKS - ms1 := peer.GC.MetadataStore() - - devicesPK := make([]crypto.PubKey, expectedNewDevices) - deviceSecrets := make([]*protocoltypes.DeviceSecret, expectedNewDevices) - - ch := peer.GC.FillMessageKeysHolderUsingNewData() - - for i := 0; i < expectedNewDevices; i++ { - devicesPK[i], deviceSecrets[i] = addDummyMemberInMetadataStore(ctx, t, ms1, peer.GC.Group(), peer.GC.MemberPubKey(), true) - } - - i := 0 - for range ch { - i++ - if i == expectedNewDevices { - break - } - } - - for i, dPK := range devicesPK { - ds, err := mkh1.GetDeviceChainKey(ctx, gSK.GetPublic(), dPK) - if assert.NoError(t, err) { - assert.Equal(t, deviceSecrets[i].Counter+uint64(mkh1.GetPrecomputedKeyExpectedCount()), ds.Counter) - // Not testing chain key value as we need to derive it from the generated value - } - } -} - -func TestMessageKeyHolderSubscription(t *testing.T) { - for _, testCase := range []struct { - expectedNewDevices int - slow bool - }{ - { - expectedNewDevices: 2, - slow: false, - }, - { - expectedNewDevices: 10, - slow: true, - }, - } { - testMessageKeyHolderSubscription(t, testCase.expectedNewDevices, testCase.slow) - } -} - -func Test_PushGroupReferences(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - logger := zap.NewNop() - - g, _, err := weshnet.NewGroupMultiMember() - assert.NoError(t, err) - - acc := cryptoutil.NewDeviceKeystore(keystore.NewMemKeystore(), nil) - - omd, err := acc.MemberDeviceForGroup(g) - assert.NoError(t, err) - - devicePK, err := omd.Public().Device.Raw() - assert.NoError(t, err) - - ds, err := cryptoutil.NewDeviceSecret() - assert.NoError(t, err) - - mkh, cleanup := cryptoutil.NewInMemMessageKeystore(logger) - defer cleanup() - - // test with the ds counter - updateAndTestPushGroupReferences(ctx, mkh, devicePK, ds.Counter, g, t) - - // do the same test with a new device secret counter - // so we can test if old references are deleted - updateAndTestPushGroupReferences(ctx, mkh, devicePK, ds.Counter+10, g, t) -} - -func updateAndTestPushGroupReferences(ctx context.Context, mkh *cryptoutil.MessageKeystore, devicePK []byte, counter uint64, g *protocoltypes.Group, t *testing.T) { - // get the group push secret, used to create the push group references - groupPushSecret, err := cryptoutil.GetGroupPushSecret(g) - assert.NoError(t, err) - - // update the push group references - err = mkh.UpdatePushGroupReferences(ctx, devicePK, counter, g) - assert.NoError(t, err) - - // test that the push group references are updated - // refs start counter - 100 to counter + 100 - start := counter - precomputePushRefsCount - end := counter + precomputePushRefsCount - - for i := start; i < end; i++ { - // compute the push group reference - pushGroupRef, err := cryptoutil.CreatePushGroupReference(devicePK, i, groupPushSecret) - assert.NoError(t, err) - - _, err = mkh.GetByPushGroupReference(ctx, pushGroupRef) - assert.NoError(t, err) - } - - // test boundary conditions - - // before the start counter - { - before := counter - precomputePushRefsCount - 1 - pushGroupRef, err := cryptoutil.CreatePushGroupReference(devicePK, before, groupPushSecret) - assert.NoError(t, err) - _, err = mkh.GetByPushGroupReference(ctx, pushGroupRef) - assert.Error(t, err) - } - - // after the end counter - { - end := counter + precomputePushRefsCount + 1 - pushGroupRef, err := cryptoutil.CreatePushGroupReference(devicePK, end, groupPushSecret) - assert.NoError(t, err) - _, err = mkh.GetByPushGroupReference(ctx, pushGroupRef) - assert.Error(t, err) - } -} - -// openEnvelope opens a MessageEnvelope and returns the decrypted message. -// It performs all the necessary steps to decrypt the message. -func openEnvelope(ctx context.Context, t testing.TB, mk *cryptoutil.MessageKeystore, g *protocoltypes.Group, ownPK crypto.PubKey, data []byte, id cid.Cid) (*protocoltypes.MessageHeaders, *protocoltypes.EncryptedMessage, error) { - t.Helper() - - assert.NotNil(t, mk) - assert.NotNil(t, g) - - env, headers, err := cryptoutil.OpenEnvelopeHeaders(data, g) - assert.NoError(t, err) - - msg, err := mk.OpenEnvelopePayload(ctx, env, headers, g, ownPK, id) - assert.NoError(t, err) - - return headers, msg, nil -} diff --git a/pkg/cryptoutil/signer_wrapper.go b/pkg/cryptoutil/signer_wrapper.go new file mode 100644 index 00000000..1f88f998 --- /dev/null +++ b/pkg/cryptoutil/signer_wrapper.go @@ -0,0 +1,28 @@ +package cryptoutil + +import ( + stdcrypto "crypto" + "io" + + libp2p_ci "github.com/libp2p/go-libp2p/core/crypto" +) + +func NewFuncSigner(key libp2p_ci.PubKey, signer func([]byte) ([]byte, error)) stdcrypto.Signer { + return &funcSigner{ + pubKey: key, + signer: signer, + } +} + +type funcSigner struct { + pubKey libp2p_ci.PubKey + signer func([]byte) ([]byte, error) +} + +func (f *funcSigner) Public() stdcrypto.PublicKey { + return f.pubKey +} + +func (f *funcSigner) Sign(_ io.Reader, digest []byte, _ stdcrypto.SignerOpts) (signature []byte, err error) { + return f.signer(digest) +} diff --git a/pkg/protocoltypes/group.go b/pkg/protocoltypes/group.go index 4d4b2b71..2f8631d4 100644 --- a/pkg/protocoltypes/group.go +++ b/pkg/protocoltypes/group.go @@ -1,11 +1,16 @@ package protocoltypes import ( + crand "crypto/rand" "encoding/hex" + "io" "github.com/libp2p/go-libp2p/core/crypto" "golang.org/x/crypto/ed25519" + "golang.org/x/crypto/hkdf" + "golang.org/x/crypto/sha3" + "berty.tech/weshnet/pkg/cryptoutil" "berty.tech/weshnet/pkg/errcode" ) @@ -73,3 +78,87 @@ func (m *Group) Copy() *Group { SignPub: m.SignPub, } } + +const CurrentGroupVersion = 1 + +// NewGroupMultiMember creates a new Group object and an invitation to be used by +// the first member of the group +func NewGroupMultiMember() (*Group, crypto.PrivKey, error) { + priv, pub, err := crypto.GenerateEd25519Key(crand.Reader) + if err != nil { + return nil, nil, errcode.ErrCryptoKeyGeneration.Wrap(err) + } + + pubBytes, err := pub.Raw() + if err != nil { + return nil, nil, errcode.ErrSerialization.Wrap(err) + } + + signing, _, err := crypto.GenerateEd25519Key(crand.Reader) + if err != nil { + return nil, nil, errcode.ErrCryptoKeyGeneration.Wrap(err) + } + + signingBytes, err := cryptoutil.SeedFromEd25519PrivateKey(signing) + if err != nil { + return nil, nil, errcode.ErrSerialization.Wrap(err) + } + + skSig, err := priv.Sign(signingBytes) + if err != nil { + return nil, nil, errcode.ErrCryptoSignature.Wrap(err) + } + + group := &Group{ + PublicKey: pubBytes, + Secret: signingBytes, + SecretSig: skSig, + GroupType: GroupTypeMultiMember, + } + + updateKey, err := group.GetLinkKeyArray() + if err != nil { + return nil, nil, errcode.ErrCryptoKeyGeneration.Wrap(err) + } + + linkKeySig, err := priv.Sign(updateKey[:]) + if err != nil { + return nil, nil, errcode.ErrCryptoSignature.Wrap(err) + } + + group.LinkKeySig = linkKeySig + + return group, priv, nil +} + +func ComputeLinkKey(publicKey, secret []byte) (*[cryptoutil.KeySize]byte, error) { + arr := [cryptoutil.KeySize]byte{} + + kdf := hkdf.New(sha3.New256, secret, nil, publicKey) + if _, err := io.ReadFull(kdf, arr[:]); err != nil { + return nil, errcode.ErrStreamRead.Wrap(err) + } + + return &arr, nil +} + +func (m *Group) GetLinkKeyArray() (*[cryptoutil.KeySize]byte, error) { + if len(m.GetLinkKey()) == cryptoutil.KeySize { + arr := [cryptoutil.KeySize]byte{} + + for i, c := range m.GetLinkKey() { + arr[i] = c + } + + return &arr, nil + } + + return ComputeLinkKey(m.GetPublicKey(), m.GetSecret()) +} + +func (m *Group) GetSharedSecret() *[cryptoutil.KeySize]byte { + sharedSecret := [cryptoutil.KeySize]byte{} + copy(sharedSecret[:], m.GetSecret()) + + return &sharedSecret +} diff --git a/pkg/protocoltypes/protocoltypes.pb.go b/pkg/protocoltypes/protocoltypes.pb.go index d0b1c55f..86aae97c 100644 --- a/pkg/protocoltypes/protocoltypes.pb.go +++ b/pkg/protocoltypes/protocoltypes.pb.go @@ -67,8 +67,8 @@ const ( EventTypeUndefined EventType = 0 // EventTypeGroupMemberDeviceAdded indicates the payload includes that a member has added their device to the group EventTypeGroupMemberDeviceAdded EventType = 1 - // EventTypeGroupDeviceSecretAdded indicates the payload includes that a member has sent their device secret to another member - EventTypeGroupDeviceSecretAdded EventType = 2 + // EventTypeGroupDeviceChainKeyAdded indicates the payload includes that a member has sent their device chain key to another member + EventTypeGroupDeviceChainKeyAdded EventType = 2 // EventTypeAccountGroupJoined indicates the payload includes that the account has joined a group EventTypeAccountGroupJoined EventType = 101 // EventTypeAccountGroupLeft indicates the payload includes that the account has left a group @@ -122,7 +122,7 @@ const ( var EventType_name = map[int32]string{ 0: "EventTypeUndefined", 1: "EventTypeGroupMemberDeviceAdded", - 2: "EventTypeGroupDeviceSecretAdded", + 2: "EventTypeGroupDeviceChainKeyAdded", 101: "EventTypeAccountGroupJoined", 102: "EventTypeAccountGroupLeft", 103: "EventTypeAccountContactRequestDisabled", @@ -152,7 +152,7 @@ var EventType_name = map[int32]string{ var EventType_value = map[string]int32{ "EventTypeUndefined": 0, "EventTypeGroupMemberDeviceAdded": 1, - "EventTypeGroupDeviceSecretAdded": 2, + "EventTypeGroupDeviceChainKeyAdded": 2, "EventTypeAccountGroupJoined": 101, "EventTypeAccountGroupLeft": 102, "EventTypeAccountContactRequestDisabled": 103, @@ -500,7 +500,7 @@ type Group struct { Secret []byte `protobuf:"bytes,2,opt,name=secret,proto3" json:"secret,omitempty"` // secret_sig is the signature of the secret used to ensure the validity of the group SecretSig []byte `protobuf:"bytes,3,opt,name=secret_sig,json=secretSig,proto3" json:"secret_sig,omitempty"` - // group_type specifies the type of the group, used to determine how device secrets are generated + // group_type specifies the type of the group, used to determine how device chain key is generated GroupType GroupType `protobuf:"varint,4,opt,name=group_type,json=groupType,proto3,enum=weshnet.protocol.v1.GroupType" json:"group_type,omitempty"` // sign_pub is the signature public key used to verify entries, not required when secret and secret_sig are provided SignPub []byte `protobuf:"bytes,5,opt,name=sign_pub,json=signPub,proto3" json:"sign_pub,omitempty"` @@ -1237,7 +1237,7 @@ func (m *ContactAddAliasKey) GetAliasPK() []byte { } // GroupAddMemberDevice is an event which indicates to a group a new device (and eventually a new member) is joining it -// When added on AccountGroup, this event should be followed by appropriate GroupAddMemberDevice and GroupAddDeviceSecret events +// When added on AccountGroup, this event should be followed by appropriate GroupAddMemberDevice and GroupAddDeviceChainKey events type GroupAddMemberDevice struct { // member_pk is the member sending the event MemberPK []byte `protobuf:"bytes,1,opt,name=member_pk,json=memberPk,proto3" json:"member_pk,omitempty"` @@ -1304,8 +1304,8 @@ func (m *GroupAddMemberDevice) GetMemberSig() []byte { return nil } -// DeviceSecret is encrypted for a specific member of the group -type DeviceSecret struct { +// DeviceChainKey is a chain key, which will be encrypted for a specific member of the group +type DeviceChainKey struct { // chain_key is the current value of the chain key of the group device ChainKey []byte `protobuf:"bytes,1,opt,name=chain_key,json=chainKey,proto3" json:"chain_key,omitempty"` // counter is the current value of the counter of the group device @@ -1315,18 +1315,18 @@ type DeviceSecret struct { XXX_sizecache int32 `json:"-"` } -func (m *DeviceSecret) Reset() { *m = DeviceSecret{} } -func (m *DeviceSecret) String() string { return proto.CompactTextString(m) } -func (*DeviceSecret) ProtoMessage() {} -func (*DeviceSecret) Descriptor() ([]byte, []int) { +func (m *DeviceChainKey) Reset() { *m = DeviceChainKey{} } +func (m *DeviceChainKey) String() string { return proto.CompactTextString(m) } +func (*DeviceChainKey) ProtoMessage() {} +func (*DeviceChainKey) Descriptor() ([]byte, []int) { return fileDescriptor_8aa93e54ccb19003, []int{13} } -func (m *DeviceSecret) XXX_Unmarshal(b []byte) error { +func (m *DeviceChainKey) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } -func (m *DeviceSecret) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { +func (m *DeviceChainKey) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { - return xxx_messageInfo_DeviceSecret.Marshal(b, m, deterministic) + return xxx_messageInfo_DeviceChainKey.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) @@ -1336,34 +1336,34 @@ func (m *DeviceSecret) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) return b[:n], nil } } -func (m *DeviceSecret) XXX_Merge(src proto.Message) { - xxx_messageInfo_DeviceSecret.Merge(m, src) +func (m *DeviceChainKey) XXX_Merge(src proto.Message) { + xxx_messageInfo_DeviceChainKey.Merge(m, src) } -func (m *DeviceSecret) XXX_Size() int { +func (m *DeviceChainKey) XXX_Size() int { return m.Size() } -func (m *DeviceSecret) XXX_DiscardUnknown() { - xxx_messageInfo_DeviceSecret.DiscardUnknown(m) +func (m *DeviceChainKey) XXX_DiscardUnknown() { + xxx_messageInfo_DeviceChainKey.DiscardUnknown(m) } -var xxx_messageInfo_DeviceSecret proto.InternalMessageInfo +var xxx_messageInfo_DeviceChainKey proto.InternalMessageInfo -func (m *DeviceSecret) GetChainKey() []byte { +func (m *DeviceChainKey) GetChainKey() []byte { if m != nil { return m.ChainKey } return nil } -func (m *DeviceSecret) GetCounter() uint64 { +func (m *DeviceChainKey) GetCounter() uint64 { if m != nil { return m.Counter } return 0 } -// GroupAddDeviceSecret is an event which indicates to a group member a device secret -type GroupAddDeviceSecret struct { +// GroupAddDeviceChainKey is an event which indicates to a group member a device chain key +type GroupAddDeviceChainKey struct { // device_pk is the device sending the event, signs the message DevicePK []byte `protobuf:"bytes,1,opt,name=device_pk,json=devicePk,proto3" json:"device_pk,omitempty"` // dest_member_pk is the member who should receive the secret @@ -1375,18 +1375,18 @@ type GroupAddDeviceSecret struct { XXX_sizecache int32 `json:"-"` } -func (m *GroupAddDeviceSecret) Reset() { *m = GroupAddDeviceSecret{} } -func (m *GroupAddDeviceSecret) String() string { return proto.CompactTextString(m) } -func (*GroupAddDeviceSecret) ProtoMessage() {} -func (*GroupAddDeviceSecret) Descriptor() ([]byte, []int) { +func (m *GroupAddDeviceChainKey) Reset() { *m = GroupAddDeviceChainKey{} } +func (m *GroupAddDeviceChainKey) String() string { return proto.CompactTextString(m) } +func (*GroupAddDeviceChainKey) ProtoMessage() {} +func (*GroupAddDeviceChainKey) Descriptor() ([]byte, []int) { return fileDescriptor_8aa93e54ccb19003, []int{14} } -func (m *GroupAddDeviceSecret) XXX_Unmarshal(b []byte) error { +func (m *GroupAddDeviceChainKey) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } -func (m *GroupAddDeviceSecret) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { +func (m *GroupAddDeviceChainKey) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { - return xxx_messageInfo_GroupAddDeviceSecret.Marshal(b, m, deterministic) + return xxx_messageInfo_GroupAddDeviceChainKey.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) @@ -1396,33 +1396,33 @@ func (m *GroupAddDeviceSecret) XXX_Marshal(b []byte, deterministic bool) ([]byte return b[:n], nil } } -func (m *GroupAddDeviceSecret) XXX_Merge(src proto.Message) { - xxx_messageInfo_GroupAddDeviceSecret.Merge(m, src) +func (m *GroupAddDeviceChainKey) XXX_Merge(src proto.Message) { + xxx_messageInfo_GroupAddDeviceChainKey.Merge(m, src) } -func (m *GroupAddDeviceSecret) XXX_Size() int { +func (m *GroupAddDeviceChainKey) XXX_Size() int { return m.Size() } -func (m *GroupAddDeviceSecret) XXX_DiscardUnknown() { - xxx_messageInfo_GroupAddDeviceSecret.DiscardUnknown(m) +func (m *GroupAddDeviceChainKey) XXX_DiscardUnknown() { + xxx_messageInfo_GroupAddDeviceChainKey.DiscardUnknown(m) } -var xxx_messageInfo_GroupAddDeviceSecret proto.InternalMessageInfo +var xxx_messageInfo_GroupAddDeviceChainKey proto.InternalMessageInfo -func (m *GroupAddDeviceSecret) GetDevicePK() []byte { +func (m *GroupAddDeviceChainKey) GetDevicePK() []byte { if m != nil { return m.DevicePK } return nil } -func (m *GroupAddDeviceSecret) GetDestMemberPK() []byte { +func (m *GroupAddDeviceChainKey) GetDestMemberPK() []byte { if m != nil { return m.DestMemberPK } return nil } -func (m *GroupAddDeviceSecret) GetPayload() []byte { +func (m *GroupAddDeviceChainKey) GetPayload() []byte { if m != nil { return m.Payload } @@ -1995,7 +1995,7 @@ func (m *AccountContactRequestReferenceReset) GetPublicRendezvousSeed() []byte { // This event should be followed by an AccountGroupJoined event // This event should be followed by a GroupAddMemberDevice event within the AccountGroup -// This event should be followed by a GroupAddDeviceSecret event within the AccountGroup +// This event should be followed by a GroupAddDeviceChainKey event within the AccountGroup // AccountContactRequestEnqueued indicates that the account will attempt to send a contact request when a matching peer is discovered type AccountContactRequestEnqueued struct { // device_pk is the device sending the event, signs the message @@ -2267,7 +2267,7 @@ func (m *AccountContactRequestDiscarded) GetContactPK() []byte { } // This event should be followed by an AccountGroupJoined event -// This event should be followed by GroupAddMemberDevice and GroupAddDeviceSecret events within the AccountGroup +// This event should be followed by GroupAddMemberDevice and GroupAddDeviceChainKey events within the AccountGroup // AccountContactRequestAccepted indicates that a contact request has been accepted type AccountContactRequestAccepted struct { // device_pk is the device sending the event, signs the message @@ -10048,24 +10048,24 @@ func (m *PushMemberTokenUpdate) GetDevicePK() []byte { return nil } -type PushReceive struct { +type OutOfStoreReceive struct { XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } -func (m *PushReceive) Reset() { *m = PushReceive{} } -func (m *PushReceive) String() string { return proto.CompactTextString(m) } -func (*PushReceive) ProtoMessage() {} -func (*PushReceive) Descriptor() ([]byte, []int) { +func (m *OutOfStoreReceive) Reset() { *m = OutOfStoreReceive{} } +func (m *OutOfStoreReceive) String() string { return proto.CompactTextString(m) } +func (*OutOfStoreReceive) ProtoMessage() {} +func (*OutOfStoreReceive) Descriptor() ([]byte, []int) { return fileDescriptor_8aa93e54ccb19003, []int{91} } -func (m *PushReceive) XXX_Unmarshal(b []byte) error { +func (m *OutOfStoreReceive) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } -func (m *PushReceive) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { +func (m *OutOfStoreReceive) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { - return xxx_messageInfo_PushReceive.Marshal(b, m, deterministic) + return xxx_messageInfo_OutOfStoreReceive.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) @@ -10075,37 +10075,37 @@ func (m *PushReceive) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) return b[:n], nil } } -func (m *PushReceive) XXX_Merge(src proto.Message) { - xxx_messageInfo_PushReceive.Merge(m, src) +func (m *OutOfStoreReceive) XXX_Merge(src proto.Message) { + xxx_messageInfo_OutOfStoreReceive.Merge(m, src) } -func (m *PushReceive) XXX_Size() int { +func (m *OutOfStoreReceive) XXX_Size() int { return m.Size() } -func (m *PushReceive) XXX_DiscardUnknown() { - xxx_messageInfo_PushReceive.DiscardUnknown(m) +func (m *OutOfStoreReceive) XXX_DiscardUnknown() { + xxx_messageInfo_OutOfStoreReceive.DiscardUnknown(m) } -var xxx_messageInfo_PushReceive proto.InternalMessageInfo +var xxx_messageInfo_OutOfStoreReceive proto.InternalMessageInfo -type PushReceive_Request struct { +type OutOfStoreReceive_Request struct { Payload []byte `protobuf:"bytes,1,opt,name=payload,proto3" json:"payload,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } -func (m *PushReceive_Request) Reset() { *m = PushReceive_Request{} } -func (m *PushReceive_Request) String() string { return proto.CompactTextString(m) } -func (*PushReceive_Request) ProtoMessage() {} -func (*PushReceive_Request) Descriptor() ([]byte, []int) { +func (m *OutOfStoreReceive_Request) Reset() { *m = OutOfStoreReceive_Request{} } +func (m *OutOfStoreReceive_Request) String() string { return proto.CompactTextString(m) } +func (*OutOfStoreReceive_Request) ProtoMessage() {} +func (*OutOfStoreReceive_Request) Descriptor() ([]byte, []int) { return fileDescriptor_8aa93e54ccb19003, []int{91, 0} } -func (m *PushReceive_Request) XXX_Unmarshal(b []byte) error { +func (m *OutOfStoreReceive_Request) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } -func (m *PushReceive_Request) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { +func (m *OutOfStoreReceive_Request) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { - return xxx_messageInfo_PushReceive_Request.Marshal(b, m, deterministic) + return xxx_messageInfo_OutOfStoreReceive_Request.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) @@ -10115,26 +10115,26 @@ func (m *PushReceive_Request) XXX_Marshal(b []byte, deterministic bool) ([]byte, return b[:n], nil } } -func (m *PushReceive_Request) XXX_Merge(src proto.Message) { - xxx_messageInfo_PushReceive_Request.Merge(m, src) +func (m *OutOfStoreReceive_Request) XXX_Merge(src proto.Message) { + xxx_messageInfo_OutOfStoreReceive_Request.Merge(m, src) } -func (m *PushReceive_Request) XXX_Size() int { +func (m *OutOfStoreReceive_Request) XXX_Size() int { return m.Size() } -func (m *PushReceive_Request) XXX_DiscardUnknown() { - xxx_messageInfo_PushReceive_Request.DiscardUnknown(m) +func (m *OutOfStoreReceive_Request) XXX_DiscardUnknown() { + xxx_messageInfo_OutOfStoreReceive_Request.DiscardUnknown(m) } -var xxx_messageInfo_PushReceive_Request proto.InternalMessageInfo +var xxx_messageInfo_OutOfStoreReceive_Request proto.InternalMessageInfo -func (m *PushReceive_Request) GetPayload() []byte { +func (m *OutOfStoreReceive_Request) GetPayload() []byte { if m != nil { return m.Payload } return nil } -type PushReceive_Reply struct { +type OutOfStoreReceive_Reply struct { Message *OutOfStoreMessage `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` Cleartext []byte `protobuf:"bytes,2,opt,name=cleartext,proto3" json:"cleartext,omitempty"` GroupPublicKey []byte `protobuf:"bytes,3,opt,name=group_public_key,json=groupPublicKey,proto3" json:"group_public_key,omitempty"` @@ -10144,18 +10144,18 @@ type PushReceive_Reply struct { XXX_sizecache int32 `json:"-"` } -func (m *PushReceive_Reply) Reset() { *m = PushReceive_Reply{} } -func (m *PushReceive_Reply) String() string { return proto.CompactTextString(m) } -func (*PushReceive_Reply) ProtoMessage() {} -func (*PushReceive_Reply) Descriptor() ([]byte, []int) { +func (m *OutOfStoreReceive_Reply) Reset() { *m = OutOfStoreReceive_Reply{} } +func (m *OutOfStoreReceive_Reply) String() string { return proto.CompactTextString(m) } +func (*OutOfStoreReceive_Reply) ProtoMessage() {} +func (*OutOfStoreReceive_Reply) Descriptor() ([]byte, []int) { return fileDescriptor_8aa93e54ccb19003, []int{91, 1} } -func (m *PushReceive_Reply) XXX_Unmarshal(b []byte) error { +func (m *OutOfStoreReceive_Reply) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } -func (m *PushReceive_Reply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { +func (m *OutOfStoreReceive_Reply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { - return xxx_messageInfo_PushReceive_Reply.Marshal(b, m, deterministic) + return xxx_messageInfo_OutOfStoreReceive_Reply.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) @@ -10165,64 +10165,64 @@ func (m *PushReceive_Reply) XXX_Marshal(b []byte, deterministic bool) ([]byte, e return b[:n], nil } } -func (m *PushReceive_Reply) XXX_Merge(src proto.Message) { - xxx_messageInfo_PushReceive_Reply.Merge(m, src) +func (m *OutOfStoreReceive_Reply) XXX_Merge(src proto.Message) { + xxx_messageInfo_OutOfStoreReceive_Reply.Merge(m, src) } -func (m *PushReceive_Reply) XXX_Size() int { +func (m *OutOfStoreReceive_Reply) XXX_Size() int { return m.Size() } -func (m *PushReceive_Reply) XXX_DiscardUnknown() { - xxx_messageInfo_PushReceive_Reply.DiscardUnknown(m) +func (m *OutOfStoreReceive_Reply) XXX_DiscardUnknown() { + xxx_messageInfo_OutOfStoreReceive_Reply.DiscardUnknown(m) } -var xxx_messageInfo_PushReceive_Reply proto.InternalMessageInfo +var xxx_messageInfo_OutOfStoreReceive_Reply proto.InternalMessageInfo -func (m *PushReceive_Reply) GetMessage() *OutOfStoreMessage { +func (m *OutOfStoreReceive_Reply) GetMessage() *OutOfStoreMessage { if m != nil { return m.Message } return nil } -func (m *PushReceive_Reply) GetCleartext() []byte { +func (m *OutOfStoreReceive_Reply) GetCleartext() []byte { if m != nil { return m.Cleartext } return nil } -func (m *PushReceive_Reply) GetGroupPublicKey() []byte { +func (m *OutOfStoreReceive_Reply) GetGroupPublicKey() []byte { if m != nil { return m.GroupPublicKey } return nil } -func (m *PushReceive_Reply) GetAlreadyReceived() bool { +func (m *OutOfStoreReceive_Reply) GetAlreadyReceived() bool { if m != nil { return m.AlreadyReceived } return false } -type PushSend struct { +type OutOfStoreSeal struct { XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } -func (m *PushSend) Reset() { *m = PushSend{} } -func (m *PushSend) String() string { return proto.CompactTextString(m) } -func (*PushSend) ProtoMessage() {} -func (*PushSend) Descriptor() ([]byte, []int) { +func (m *OutOfStoreSeal) Reset() { *m = OutOfStoreSeal{} } +func (m *OutOfStoreSeal) String() string { return proto.CompactTextString(m) } +func (*OutOfStoreSeal) ProtoMessage() {} +func (*OutOfStoreSeal) Descriptor() ([]byte, []int) { return fileDescriptor_8aa93e54ccb19003, []int{92} } -func (m *PushSend) XXX_Unmarshal(b []byte) error { +func (m *OutOfStoreSeal) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } -func (m *PushSend) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { +func (m *OutOfStoreSeal) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { - return xxx_messageInfo_PushSend.Marshal(b, m, deterministic) + return xxx_messageInfo_OutOfStoreSeal.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) @@ -10232,188 +10232,38 @@ func (m *PushSend) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return b[:n], nil } } -func (m *PushSend) XXX_Merge(src proto.Message) { - xxx_messageInfo_PushSend.Merge(m, src) +func (m *OutOfStoreSeal) XXX_Merge(src proto.Message) { + xxx_messageInfo_OutOfStoreSeal.Merge(m, src) } -func (m *PushSend) XXX_Size() int { +func (m *OutOfStoreSeal) XXX_Size() int { return m.Size() } -func (m *PushSend) XXX_DiscardUnknown() { - xxx_messageInfo_PushSend.DiscardUnknown(m) +func (m *OutOfStoreSeal) XXX_DiscardUnknown() { + xxx_messageInfo_OutOfStoreSeal.DiscardUnknown(m) } -var xxx_messageInfo_PushSend proto.InternalMessageInfo +var xxx_messageInfo_OutOfStoreSeal proto.InternalMessageInfo -type PushSend_Request struct { - CID []byte `protobuf:"bytes,1,opt,name=cid,proto3" json:"cid,omitempty"` - GroupPublicKey []byte `protobuf:"bytes,2,opt,name=group_public_key,json=groupPublicKey,proto3" json:"group_public_key,omitempty"` - GroupMembers []*MemberWithDevices `protobuf:"bytes,3,rep,name=group_members,json=groupMembers,proto3" json:"group_members,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *PushSend_Request) Reset() { *m = PushSend_Request{} } -func (m *PushSend_Request) String() string { return proto.CompactTextString(m) } -func (*PushSend_Request) ProtoMessage() {} -func (*PushSend_Request) Descriptor() ([]byte, []int) { - return fileDescriptor_8aa93e54ccb19003, []int{92, 0} -} -func (m *PushSend_Request) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *PushSend_Request) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_PushSend_Request.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalToSizedBuffer(b) - if err != nil { - return nil, err - } - return b[:n], nil - } -} -func (m *PushSend_Request) XXX_Merge(src proto.Message) { - xxx_messageInfo_PushSend_Request.Merge(m, src) -} -func (m *PushSend_Request) XXX_Size() int { - return m.Size() -} -func (m *PushSend_Request) XXX_DiscardUnknown() { - xxx_messageInfo_PushSend_Request.DiscardUnknown(m) -} - -var xxx_messageInfo_PushSend_Request proto.InternalMessageInfo - -func (m *PushSend_Request) GetCID() []byte { - if m != nil { - return m.CID - } - return nil -} - -func (m *PushSend_Request) GetGroupPublicKey() []byte { - if m != nil { - return m.GroupPublicKey - } - return nil -} - -func (m *PushSend_Request) GetGroupMembers() []*MemberWithDevices { - if m != nil { - return m.GroupMembers - } - return nil -} - -type PushSend_Reply struct { - GroupMembers []*MemberWithDevices `protobuf:"bytes,1,rep,name=group_members,json=groupMembers,proto3" json:"group_members,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *PushSend_Reply) Reset() { *m = PushSend_Reply{} } -func (m *PushSend_Reply) String() string { return proto.CompactTextString(m) } -func (*PushSend_Reply) ProtoMessage() {} -func (*PushSend_Reply) Descriptor() ([]byte, []int) { - return fileDescriptor_8aa93e54ccb19003, []int{92, 1} -} -func (m *PushSend_Reply) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *PushSend_Reply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_PushSend_Reply.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalToSizedBuffer(b) - if err != nil { - return nil, err - } - return b[:n], nil - } -} -func (m *PushSend_Reply) XXX_Merge(src proto.Message) { - xxx_messageInfo_PushSend_Reply.Merge(m, src) -} -func (m *PushSend_Reply) XXX_Size() int { - return m.Size() -} -func (m *PushSend_Reply) XXX_DiscardUnknown() { - xxx_messageInfo_PushSend_Reply.DiscardUnknown(m) -} - -var xxx_messageInfo_PushSend_Reply proto.InternalMessageInfo - -func (m *PushSend_Reply) GetGroupMembers() []*MemberWithDevices { - if m != nil { - return m.GroupMembers - } - return nil -} - -type PushShareToken struct { +type OutOfStoreSeal_Request struct { + CID []byte `protobuf:"bytes,1,opt,name=cid,proto3" json:"cid,omitempty"` + GroupPublicKey []byte `protobuf:"bytes,2,opt,name=group_public_key,json=groupPublicKey,proto3" json:"group_public_key,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } -func (m *PushShareToken) Reset() { *m = PushShareToken{} } -func (m *PushShareToken) String() string { return proto.CompactTextString(m) } -func (*PushShareToken) ProtoMessage() {} -func (*PushShareToken) Descriptor() ([]byte, []int) { - return fileDescriptor_8aa93e54ccb19003, []int{93} -} -func (m *PushShareToken) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *PushShareToken) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_PushShareToken.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalToSizedBuffer(b) - if err != nil { - return nil, err - } - return b[:n], nil - } -} -func (m *PushShareToken) XXX_Merge(src proto.Message) { - xxx_messageInfo_PushShareToken.Merge(m, src) -} -func (m *PushShareToken) XXX_Size() int { - return m.Size() -} -func (m *PushShareToken) XXX_DiscardUnknown() { - xxx_messageInfo_PushShareToken.DiscardUnknown(m) -} - -var xxx_messageInfo_PushShareToken proto.InternalMessageInfo - -type PushShareToken_Request struct { - GroupPK []byte `protobuf:"bytes,1,opt,name=group_pk,json=groupPk,proto3" json:"group_pk,omitempty"` - Server *PushServer `protobuf:"bytes,2,opt,name=server,proto3" json:"server,omitempty"` - Receiver *PushServiceReceiver `protobuf:"bytes,3,opt,name=receiver,proto3" json:"receiver,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *PushShareToken_Request) Reset() { *m = PushShareToken_Request{} } -func (m *PushShareToken_Request) String() string { return proto.CompactTextString(m) } -func (*PushShareToken_Request) ProtoMessage() {} -func (*PushShareToken_Request) Descriptor() ([]byte, []int) { - return fileDescriptor_8aa93e54ccb19003, []int{93, 0} +func (m *OutOfStoreSeal_Request) Reset() { *m = OutOfStoreSeal_Request{} } +func (m *OutOfStoreSeal_Request) String() string { return proto.CompactTextString(m) } +func (*OutOfStoreSeal_Request) ProtoMessage() {} +func (*OutOfStoreSeal_Request) Descriptor() ([]byte, []int) { + return fileDescriptor_8aa93e54ccb19003, []int{92, 0} } -func (m *PushShareToken_Request) XXX_Unmarshal(b []byte) error { +func (m *OutOfStoreSeal_Request) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } -func (m *PushShareToken_Request) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { +func (m *OutOfStoreSeal_Request) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { - return xxx_messageInfo_PushShareToken_Request.Marshal(b, m, deterministic) + return xxx_messageInfo_OutOfStoreSeal_Request.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) @@ -10423,261 +10273,51 @@ func (m *PushShareToken_Request) XXX_Marshal(b []byte, deterministic bool) ([]by return b[:n], nil } } -func (m *PushShareToken_Request) XXX_Merge(src proto.Message) { - xxx_messageInfo_PushShareToken_Request.Merge(m, src) +func (m *OutOfStoreSeal_Request) XXX_Merge(src proto.Message) { + xxx_messageInfo_OutOfStoreSeal_Request.Merge(m, src) } -func (m *PushShareToken_Request) XXX_Size() int { +func (m *OutOfStoreSeal_Request) XXX_Size() int { return m.Size() } -func (m *PushShareToken_Request) XXX_DiscardUnknown() { - xxx_messageInfo_PushShareToken_Request.DiscardUnknown(m) -} - -var xxx_messageInfo_PushShareToken_Request proto.InternalMessageInfo - -func (m *PushShareToken_Request) GetGroupPK() []byte { - if m != nil { - return m.GroupPK - } - return nil +func (m *OutOfStoreSeal_Request) XXX_DiscardUnknown() { + xxx_messageInfo_OutOfStoreSeal_Request.DiscardUnknown(m) } -func (m *PushShareToken_Request) GetServer() *PushServer { - if m != nil { - return m.Server - } - return nil -} +var xxx_messageInfo_OutOfStoreSeal_Request proto.InternalMessageInfo -func (m *PushShareToken_Request) GetReceiver() *PushServiceReceiver { +func (m *OutOfStoreSeal_Request) GetCID() []byte { if m != nil { - return m.Receiver + return m.CID } return nil } -type PushShareToken_Reply struct { - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *PushShareToken_Reply) Reset() { *m = PushShareToken_Reply{} } -func (m *PushShareToken_Reply) String() string { return proto.CompactTextString(m) } -func (*PushShareToken_Reply) ProtoMessage() {} -func (*PushShareToken_Reply) Descriptor() ([]byte, []int) { - return fileDescriptor_8aa93e54ccb19003, []int{93, 1} -} -func (m *PushShareToken_Reply) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *PushShareToken_Reply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_PushShareToken_Reply.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalToSizedBuffer(b) - if err != nil { - return nil, err - } - return b[:n], nil - } -} -func (m *PushShareToken_Reply) XXX_Merge(src proto.Message) { - xxx_messageInfo_PushShareToken_Reply.Merge(m, src) -} -func (m *PushShareToken_Reply) XXX_Size() int { - return m.Size() -} -func (m *PushShareToken_Reply) XXX_DiscardUnknown() { - xxx_messageInfo_PushShareToken_Reply.DiscardUnknown(m) -} - -var xxx_messageInfo_PushShareToken_Reply proto.InternalMessageInfo - -type PushSetDeviceToken struct { - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *PushSetDeviceToken) Reset() { *m = PushSetDeviceToken{} } -func (m *PushSetDeviceToken) String() string { return proto.CompactTextString(m) } -func (*PushSetDeviceToken) ProtoMessage() {} -func (*PushSetDeviceToken) Descriptor() ([]byte, []int) { - return fileDescriptor_8aa93e54ccb19003, []int{94} -} -func (m *PushSetDeviceToken) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *PushSetDeviceToken) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_PushSetDeviceToken.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalToSizedBuffer(b) - if err != nil { - return nil, err - } - return b[:n], nil - } -} -func (m *PushSetDeviceToken) XXX_Merge(src proto.Message) { - xxx_messageInfo_PushSetDeviceToken.Merge(m, src) -} -func (m *PushSetDeviceToken) XXX_Size() int { - return m.Size() -} -func (m *PushSetDeviceToken) XXX_DiscardUnknown() { - xxx_messageInfo_PushSetDeviceToken.DiscardUnknown(m) -} - -var xxx_messageInfo_PushSetDeviceToken proto.InternalMessageInfo - -type PushSetDeviceToken_Request struct { - Receiver *PushServiceReceiver `protobuf:"bytes,1,opt,name=receiver,proto3" json:"receiver,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *PushSetDeviceToken_Request) Reset() { *m = PushSetDeviceToken_Request{} } -func (m *PushSetDeviceToken_Request) String() string { return proto.CompactTextString(m) } -func (*PushSetDeviceToken_Request) ProtoMessage() {} -func (*PushSetDeviceToken_Request) Descriptor() ([]byte, []int) { - return fileDescriptor_8aa93e54ccb19003, []int{94, 0} -} -func (m *PushSetDeviceToken_Request) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *PushSetDeviceToken_Request) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_PushSetDeviceToken_Request.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalToSizedBuffer(b) - if err != nil { - return nil, err - } - return b[:n], nil - } -} -func (m *PushSetDeviceToken_Request) XXX_Merge(src proto.Message) { - xxx_messageInfo_PushSetDeviceToken_Request.Merge(m, src) -} -func (m *PushSetDeviceToken_Request) XXX_Size() int { - return m.Size() -} -func (m *PushSetDeviceToken_Request) XXX_DiscardUnknown() { - xxx_messageInfo_PushSetDeviceToken_Request.DiscardUnknown(m) -} - -var xxx_messageInfo_PushSetDeviceToken_Request proto.InternalMessageInfo - -func (m *PushSetDeviceToken_Request) GetReceiver() *PushServiceReceiver { +func (m *OutOfStoreSeal_Request) GetGroupPublicKey() []byte { if m != nil { - return m.Receiver + return m.GroupPublicKey } return nil } -type PushSetDeviceToken_Reply struct { +type OutOfStoreSeal_Reply struct { + Encrypted []byte `protobuf:"bytes,1,opt,name=encrypted,proto3" json:"encrypted,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } -func (m *PushSetDeviceToken_Reply) Reset() { *m = PushSetDeviceToken_Reply{} } -func (m *PushSetDeviceToken_Reply) String() string { return proto.CompactTextString(m) } -func (*PushSetDeviceToken_Reply) ProtoMessage() {} -func (*PushSetDeviceToken_Reply) Descriptor() ([]byte, []int) { - return fileDescriptor_8aa93e54ccb19003, []int{94, 1} -} -func (m *PushSetDeviceToken_Reply) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *PushSetDeviceToken_Reply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_PushSetDeviceToken_Reply.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalToSizedBuffer(b) - if err != nil { - return nil, err - } - return b[:n], nil - } -} -func (m *PushSetDeviceToken_Reply) XXX_Merge(src proto.Message) { - xxx_messageInfo_PushSetDeviceToken_Reply.Merge(m, src) -} -func (m *PushSetDeviceToken_Reply) XXX_Size() int { - return m.Size() -} -func (m *PushSetDeviceToken_Reply) XXX_DiscardUnknown() { - xxx_messageInfo_PushSetDeviceToken_Reply.DiscardUnknown(m) -} - -var xxx_messageInfo_PushSetDeviceToken_Reply proto.InternalMessageInfo - -type PushSetServer struct { - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *PushSetServer) Reset() { *m = PushSetServer{} } -func (m *PushSetServer) String() string { return proto.CompactTextString(m) } -func (*PushSetServer) ProtoMessage() {} -func (*PushSetServer) Descriptor() ([]byte, []int) { - return fileDescriptor_8aa93e54ccb19003, []int{95} -} -func (m *PushSetServer) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *PushSetServer) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_PushSetServer.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalToSizedBuffer(b) - if err != nil { - return nil, err - } - return b[:n], nil - } -} -func (m *PushSetServer) XXX_Merge(src proto.Message) { - xxx_messageInfo_PushSetServer.Merge(m, src) -} -func (m *PushSetServer) XXX_Size() int { - return m.Size() -} -func (m *PushSetServer) XXX_DiscardUnknown() { - xxx_messageInfo_PushSetServer.DiscardUnknown(m) -} - -var xxx_messageInfo_PushSetServer proto.InternalMessageInfo - -type PushSetServer_Request struct { - Server *PushServer `protobuf:"bytes,1,opt,name=server,proto3" json:"server,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *PushSetServer_Request) Reset() { *m = PushSetServer_Request{} } -func (m *PushSetServer_Request) String() string { return proto.CompactTextString(m) } -func (*PushSetServer_Request) ProtoMessage() {} -func (*PushSetServer_Request) Descriptor() ([]byte, []int) { - return fileDescriptor_8aa93e54ccb19003, []int{95, 0} +func (m *OutOfStoreSeal_Reply) Reset() { *m = OutOfStoreSeal_Reply{} } +func (m *OutOfStoreSeal_Reply) String() string { return proto.CompactTextString(m) } +func (*OutOfStoreSeal_Reply) ProtoMessage() {} +func (*OutOfStoreSeal_Reply) Descriptor() ([]byte, []int) { + return fileDescriptor_8aa93e54ccb19003, []int{92, 1} } -func (m *PushSetServer_Request) XXX_Unmarshal(b []byte) error { +func (m *OutOfStoreSeal_Reply) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } -func (m *PushSetServer_Request) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { +func (m *OutOfStoreSeal_Reply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { - return xxx_messageInfo_PushSetServer_Request.Marshal(b, m, deterministic) + return xxx_messageInfo_OutOfStoreSeal_Reply.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) @@ -10687,64 +10327,25 @@ func (m *PushSetServer_Request) XXX_Marshal(b []byte, deterministic bool) ([]byt return b[:n], nil } } -func (m *PushSetServer_Request) XXX_Merge(src proto.Message) { - xxx_messageInfo_PushSetServer_Request.Merge(m, src) +func (m *OutOfStoreSeal_Reply) XXX_Merge(src proto.Message) { + xxx_messageInfo_OutOfStoreSeal_Reply.Merge(m, src) } -func (m *PushSetServer_Request) XXX_Size() int { +func (m *OutOfStoreSeal_Reply) XXX_Size() int { return m.Size() } -func (m *PushSetServer_Request) XXX_DiscardUnknown() { - xxx_messageInfo_PushSetServer_Request.DiscardUnknown(m) +func (m *OutOfStoreSeal_Reply) XXX_DiscardUnknown() { + xxx_messageInfo_OutOfStoreSeal_Reply.DiscardUnknown(m) } -var xxx_messageInfo_PushSetServer_Request proto.InternalMessageInfo +var xxx_messageInfo_OutOfStoreSeal_Reply proto.InternalMessageInfo -func (m *PushSetServer_Request) GetServer() *PushServer { +func (m *OutOfStoreSeal_Reply) GetEncrypted() []byte { if m != nil { - return m.Server + return m.Encrypted } return nil } -type PushSetServer_Reply struct { - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *PushSetServer_Reply) Reset() { *m = PushSetServer_Reply{} } -func (m *PushSetServer_Reply) String() string { return proto.CompactTextString(m) } -func (*PushSetServer_Reply) ProtoMessage() {} -func (*PushSetServer_Reply) Descriptor() ([]byte, []int) { - return fileDescriptor_8aa93e54ccb19003, []int{95, 1} -} -func (m *PushSetServer_Reply) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *PushSetServer_Reply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_PushSetServer_Reply.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalToSizedBuffer(b) - if err != nil { - return nil, err - } - return b[:n], nil - } -} -func (m *PushSetServer_Reply) XXX_Merge(src proto.Message) { - xxx_messageInfo_PushSetServer_Reply.Merge(m, src) -} -func (m *PushSetServer_Reply) XXX_Size() int { - return m.Size() -} -func (m *PushSetServer_Reply) XXX_DiscardUnknown() { - xxx_messageInfo_PushSetServer_Reply.DiscardUnknown(m) -} - -var xxx_messageInfo_PushSetServer_Reply proto.InternalMessageInfo - type FirstLastCounters struct { First uint64 `protobuf:"varint,1,opt,name=first,proto3" json:"first,omitempty"` Last uint64 `protobuf:"varint,2,opt,name=last,proto3" json:"last,omitempty"` @@ -10757,7 +10358,7 @@ func (m *FirstLastCounters) Reset() { *m = FirstLastCounters{} } func (m *FirstLastCounters) String() string { return proto.CompactTextString(m) } func (*FirstLastCounters) ProtoMessage() {} func (*FirstLastCounters) Descriptor() ([]byte, []int) { - return fileDescriptor_8aa93e54ccb19003, []int{96} + return fileDescriptor_8aa93e54ccb19003, []int{93} } func (m *FirstLastCounters) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -10815,7 +10416,7 @@ func (m *OrbitDBMessageHeads) Reset() { *m = OrbitDBMessageHeads{} } func (m *OrbitDBMessageHeads) String() string { return proto.CompactTextString(m) } func (*OrbitDBMessageHeads) ProtoMessage() {} func (*OrbitDBMessageHeads) Descriptor() ([]byte, []int) { - return fileDescriptor_8aa93e54ccb19003, []int{97} + return fileDescriptor_8aa93e54ccb19003, []int{94} } func (m *OrbitDBMessageHeads) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -10872,7 +10473,7 @@ func (m *OrbitDBMessageHeads_Box) Reset() { *m = OrbitDBMessageHeads_Box func (m *OrbitDBMessageHeads_Box) String() string { return proto.CompactTextString(m) } func (*OrbitDBMessageHeads_Box) ProtoMessage() {} func (*OrbitDBMessageHeads_Box) Descriptor() ([]byte, []int) { - return fileDescriptor_8aa93e54ccb19003, []int{97, 0} + return fileDescriptor_8aa93e54ccb19003, []int{94, 0} } func (m *OrbitDBMessageHeads_Box) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -10939,7 +10540,7 @@ func (m *RefreshContactRequest) Reset() { *m = RefreshContactRequest{} } func (m *RefreshContactRequest) String() string { return proto.CompactTextString(m) } func (*RefreshContactRequest) ProtoMessage() {} func (*RefreshContactRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_8aa93e54ccb19003, []int{98} + return fileDescriptor_8aa93e54ccb19003, []int{95} } func (m *RefreshContactRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -10982,7 +10583,7 @@ func (m *RefreshContactRequest_Peer) Reset() { *m = RefreshContactReques func (m *RefreshContactRequest_Peer) String() string { return proto.CompactTextString(m) } func (*RefreshContactRequest_Peer) ProtoMessage() {} func (*RefreshContactRequest_Peer) Descriptor() ([]byte, []int) { - return fileDescriptor_8aa93e54ccb19003, []int{98, 0} + return fileDescriptor_8aa93e54ccb19003, []int{95, 0} } func (m *RefreshContactRequest_Peer) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -11038,7 +10639,7 @@ func (m *RefreshContactRequest_Request) Reset() { *m = RefreshContactReq func (m *RefreshContactRequest_Request) String() string { return proto.CompactTextString(m) } func (*RefreshContactRequest_Request) ProtoMessage() {} func (*RefreshContactRequest_Request) Descriptor() ([]byte, []int) { - return fileDescriptor_8aa93e54ccb19003, []int{98, 1} + return fileDescriptor_8aa93e54ccb19003, []int{95, 1} } func (m *RefreshContactRequest_Request) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -11093,7 +10694,7 @@ func (m *RefreshContactRequest_Reply) Reset() { *m = RefreshContactReque func (m *RefreshContactRequest_Reply) String() string { return proto.CompactTextString(m) } func (*RefreshContactRequest_Reply) ProtoMessage() {} func (*RefreshContactRequest_Reply) Descriptor() ([]byte, []int) { - return fileDescriptor_8aa93e54ccb19003, []int{98, 2} + return fileDescriptor_8aa93e54ccb19003, []int{95, 2} } func (m *RefreshContactRequest_Reply) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -11153,8 +10754,8 @@ func init() { proto.RegisterType((*AppMetadata)(nil), "weshnet.protocol.v1.AppMetadata") proto.RegisterType((*ContactAddAliasKey)(nil), "weshnet.protocol.v1.ContactAddAliasKey") proto.RegisterType((*GroupAddMemberDevice)(nil), "weshnet.protocol.v1.GroupAddMemberDevice") - proto.RegisterType((*DeviceSecret)(nil), "weshnet.protocol.v1.DeviceSecret") - proto.RegisterType((*GroupAddDeviceSecret)(nil), "weshnet.protocol.v1.GroupAddDeviceSecret") + proto.RegisterType((*DeviceChainKey)(nil), "weshnet.protocol.v1.DeviceChainKey") + proto.RegisterType((*GroupAddDeviceChainKey)(nil), "weshnet.protocol.v1.GroupAddDeviceChainKey") proto.RegisterType((*MultiMemberGroupAddAliasResolver)(nil), "weshnet.protocol.v1.MultiMemberGroupAddAliasResolver") proto.RegisterType((*MultiMemberGrantAdminRole)(nil), "weshnet.protocol.v1.MultiMemberGrantAdminRole") proto.RegisterType((*MultiMemberInitialMember)(nil), "weshnet.protocol.v1.MultiMemberInitialMember") @@ -11320,21 +10921,12 @@ func init() { proto.RegisterType((*PushDeviceServerRegistered)(nil), "weshnet.protocol.v1.PushDeviceServerRegistered") proto.RegisterType((*AccountVerifiedCredentialRegistered)(nil), "weshnet.protocol.v1.AccountVerifiedCredentialRegistered") proto.RegisterType((*PushMemberTokenUpdate)(nil), "weshnet.protocol.v1.PushMemberTokenUpdate") - proto.RegisterType((*PushReceive)(nil), "weshnet.protocol.v1.PushReceive") - proto.RegisterType((*PushReceive_Request)(nil), "weshnet.protocol.v1.PushReceive.Request") - proto.RegisterType((*PushReceive_Reply)(nil), "weshnet.protocol.v1.PushReceive.Reply") - proto.RegisterType((*PushSend)(nil), "weshnet.protocol.v1.PushSend") - proto.RegisterType((*PushSend_Request)(nil), "weshnet.protocol.v1.PushSend.Request") - proto.RegisterType((*PushSend_Reply)(nil), "weshnet.protocol.v1.PushSend.Reply") - proto.RegisterType((*PushShareToken)(nil), "weshnet.protocol.v1.PushShareToken") - proto.RegisterType((*PushShareToken_Request)(nil), "weshnet.protocol.v1.PushShareToken.Request") - proto.RegisterType((*PushShareToken_Reply)(nil), "weshnet.protocol.v1.PushShareToken.Reply") - proto.RegisterType((*PushSetDeviceToken)(nil), "weshnet.protocol.v1.PushSetDeviceToken") - proto.RegisterType((*PushSetDeviceToken_Request)(nil), "weshnet.protocol.v1.PushSetDeviceToken.Request") - proto.RegisterType((*PushSetDeviceToken_Reply)(nil), "weshnet.protocol.v1.PushSetDeviceToken.Reply") - proto.RegisterType((*PushSetServer)(nil), "weshnet.protocol.v1.PushSetServer") - proto.RegisterType((*PushSetServer_Request)(nil), "weshnet.protocol.v1.PushSetServer.Request") - proto.RegisterType((*PushSetServer_Reply)(nil), "weshnet.protocol.v1.PushSetServer.Reply") + proto.RegisterType((*OutOfStoreReceive)(nil), "weshnet.protocol.v1.OutOfStoreReceive") + proto.RegisterType((*OutOfStoreReceive_Request)(nil), "weshnet.protocol.v1.OutOfStoreReceive.Request") + proto.RegisterType((*OutOfStoreReceive_Reply)(nil), "weshnet.protocol.v1.OutOfStoreReceive.Reply") + proto.RegisterType((*OutOfStoreSeal)(nil), "weshnet.protocol.v1.OutOfStoreSeal") + proto.RegisterType((*OutOfStoreSeal_Request)(nil), "weshnet.protocol.v1.OutOfStoreSeal.Request") + proto.RegisterType((*OutOfStoreSeal_Reply)(nil), "weshnet.protocol.v1.OutOfStoreSeal.Reply") proto.RegisterType((*FirstLastCounters)(nil), "weshnet.protocol.v1.FirstLastCounters") proto.RegisterType((*OrbitDBMessageHeads)(nil), "weshnet.protocol.v1.OrbitDBMessageHeads") proto.RegisterType((*OrbitDBMessageHeads_Box)(nil), "weshnet.protocol.v1.OrbitDBMessageHeads.Box") @@ -11347,415 +10939,406 @@ func init() { func init() { proto.RegisterFile("protocoltypes.proto", fileDescriptor_8aa93e54ccb19003) } var fileDescriptor_8aa93e54ccb19003 = []byte{ - // 6521 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe4, 0x7d, 0x5d, 0x6c, 0x1c, 0xd7, - 0x75, 0xb0, 0x67, 0x97, 0xe4, 0xee, 0x9e, 0x5d, 0x92, 0xc3, 0x2b, 0x4a, 0x5a, 0xad, 0x6d, 0x51, - 0x1e, 0x59, 0xff, 0x36, 0x25, 0xd3, 0xfe, 0xe2, 0x38, 0x8a, 0x9d, 0x50, 0x24, 0xa5, 0x8f, 0xd6, - 0xdf, 0x7a, 0x28, 0xc6, 0x71, 0x60, 0x7c, 0x9b, 0xe1, 0xcc, 0xe5, 0x72, 0xc2, 0xd9, 0x99, 0xf1, - 0xcc, 0x2c, 0x25, 0x06, 0x5f, 0x8a, 0x20, 0x89, 0x93, 0x14, 0x4d, 0xdb, 0xfc, 0x34, 0x45, 0x81, - 0x04, 0x45, 0x1f, 0xfa, 0x56, 0xa4, 0x2d, 0x8c, 0x22, 0x40, 0x81, 0x3e, 0xf5, 0xa9, 0x29, 0xd2, - 0x20, 0x4f, 0x79, 0x29, 0xc0, 0xa6, 0x7c, 0x4a, 0x81, 0xa2, 0x68, 0xd1, 0xa4, 0x2f, 0x01, 0x82, - 0xe2, 0xfe, 0xcd, 0xdc, 0xd9, 0x9d, 0xd9, 0x1f, 0x4a, 0x46, 0x1f, 0xfa, 0xa4, 0xbd, 0x67, 0xce, - 0xdf, 0x3d, 0xf7, 0xdc, 0x7b, 0xcf, 0x3d, 0xf7, 0x5c, 0x0a, 0x8e, 0xf9, 0x81, 0x17, 0x79, 0xa6, - 0xe7, 0x44, 0xfb, 0x3e, 0x0e, 0x17, 0x69, 0x0b, 0x1d, 0x7b, 0x88, 0xc3, 0x1d, 0x17, 0x47, 0x8b, - 0xe2, 0xe3, 0xe2, 0xde, 0x4b, 0x8d, 0xf9, 0xb6, 0xd7, 0xf6, 0x28, 0xe0, 0x2a, 0xf9, 0xc5, 0xbe, - 0x35, 0x4e, 0xf9, 0xdd, 0x70, 0x87, 0xd2, 0x5e, 0x8d, 0x7f, 0xb1, 0x4f, 0xda, 0x3f, 0x28, 0x50, - 0x5a, 0x36, 0x4d, 0xaf, 0xeb, 0x46, 0xe8, 0x1a, 0x4c, 0xb6, 0x03, 0xaf, 0xeb, 0xd7, 0x95, 0x33, - 0xca, 0xc5, 0xea, 0x52, 0x63, 0x31, 0x43, 0xc2, 0xe2, 0x2d, 0x82, 0xa1, 0x33, 0x44, 0xb4, 0x08, - 0xc7, 0x0c, 0x46, 0xdc, 0xf2, 0x03, 0x7b, 0xcf, 0x88, 0x70, 0x6b, 0x17, 0xef, 0xd7, 0x0b, 0x67, - 0x94, 0x8b, 0x35, 0x7d, 0x8e, 0x7f, 0x6a, 0xb2, 0x2f, 0xb7, 0xf1, 0x3e, 0xba, 0x0c, 0x73, 0x86, - 0x63, 0x1b, 0x61, 0x0a, 0xbb, 0x48, 0xb1, 0x67, 0xe9, 0x07, 0x09, 0xf7, 0x15, 0x38, 0xe1, 0x77, - 0xb7, 0x1c, 0xdb, 0x6c, 0x05, 0xd8, 0xb5, 0xf0, 0xe7, 0xf7, 0xbc, 0x6e, 0xd8, 0x0a, 0x31, 0xb6, - 0xea, 0x13, 0x94, 0x60, 0x9e, 0x7d, 0xd5, 0xe3, 0x8f, 0x1b, 0x18, 0x5b, 0xda, 0x2f, 0x15, 0x98, - 0xa4, 0x2a, 0xa2, 0x67, 0x01, 0x38, 0x3d, 0x11, 0xa2, 0x50, 0x9a, 0x0a, 0x83, 0x10, 0xf6, 0x27, - 0x60, 0x2a, 0xc4, 0x66, 0x80, 0x23, 0xae, 0x2d, 0x6f, 0x11, 0x32, 0xf6, 0xab, 0x15, 0xda, 0x6d, - 0xae, 0x5b, 0x85, 0x41, 0x36, 0xec, 0x36, 0x7a, 0x1d, 0x80, 0x76, 0xbd, 0x45, 0x8c, 0x48, 0x35, - 0x99, 0x59, 0x3a, 0x9d, 0x6f, 0xa8, 0x07, 0xfb, 0x3e, 0xd6, 0x2b, 0x6d, 0xf1, 0x13, 0x9d, 0x82, - 0x72, 0x68, 0xb7, 0xdd, 0x96, 0xdf, 0xdd, 0xaa, 0x4f, 0x52, 0xde, 0x25, 0xd2, 0x6e, 0x76, 0xb7, - 0xc8, 0x27, 0xc7, 0x76, 0x77, 0xa9, 0xb6, 0x53, 0xec, 0x13, 0x69, 0x13, 0x5d, 0xcf, 0x40, 0x4d, - 0x7c, 0xa2, 0x5a, 0x95, 0xe8, 0x67, 0xe0, 0x9f, 0x37, 0xec, 0xb6, 0xf6, 0x2b, 0x05, 0x54, 0x2a, - 0xf0, 0xff, 0x62, 0xc3, 0x0a, 0xd7, 0x1e, 0xf9, 0x5e, 0x10, 0x0d, 0xb3, 0x80, 0xac, 0x4b, 0x21, - 0xad, 0xcb, 0x1a, 0x1c, 0xeb, 0xe0, 0xc8, 0xb0, 0x8c, 0xc8, 0x68, 0xed, 0x10, 0x8e, 0x2d, 0xd3, - 0xb6, 0xc2, 0x7a, 0xf1, 0x4c, 0xf1, 0x62, 0xed, 0xc6, 0xf1, 0xc3, 0x83, 0x85, 0xb9, 0xbb, 0xfc, - 0x33, 0x95, 0xb7, 0xb2, 0xbe, 0x1a, 0xea, 0x73, 0x9d, 0x14, 0xc8, 0xb6, 0x42, 0xc6, 0x26, 0x0c, - 0x8d, 0x36, 0x0e, 0x65, 0x36, 0x13, 0x32, 0x1b, 0xf6, 0x39, 0xc5, 0x46, 0x06, 0x11, 0x36, 0xb2, - 0x65, 0x26, 0x53, 0x96, 0xd1, 0x7e, 0xa2, 0xc0, 0x34, 0xed, 0xb7, 0xd0, 0x87, 0x0c, 0x10, 0xde, - 0xc3, 0x6e, 0xc4, 0x06, 0x48, 0x19, 0x30, 0x40, 0x6b, 0x04, 0x8d, 0x0d, 0x10, 0x16, 0x3f, 0x51, - 0x1d, 0x4a, 0xbe, 0xb1, 0xef, 0x78, 0x86, 0x25, 0x6c, 0xc2, 0x9b, 0x48, 0x85, 0x62, 0xe2, 0x11, - 0xe4, 0x27, 0xd2, 0x61, 0x4e, 0xf0, 0x6b, 0x89, 0xce, 0x53, 0x97, 0xa8, 0x2e, 0x9d, 0xcb, 0x94, - 0xd8, 0xe4, 0xbf, 0x85, 0xb2, 0xba, 0xea, 0xf7, 0x40, 0xb4, 0x65, 0xde, 0x9f, 0x35, 0x77, 0x0f, - 0x3b, 0x9e, 0x8f, 0xd1, 0x3c, 0x4c, 0xba, 0x9e, 0x6b, 0x62, 0x3e, 0x7e, 0xac, 0x41, 0xa0, 0x54, - 0x67, 0xae, 0x24, 0x6b, 0xbc, 0x39, 0x51, 0x2e, 0xaa, 0x13, 0xda, 0x7f, 0x2a, 0x30, 0xc3, 0xed, - 0x4a, 0x6c, 0x88, 0x83, 0x90, 0xf4, 0x8a, 0x4e, 0x45, 0x1c, 0x50, 0x36, 0x13, 0xba, 0x68, 0xa2, - 0x4b, 0x50, 0xb1, 0xf0, 0x9e, 0x6d, 0xe2, 0x96, 0xbf, 0xcb, 0x98, 0xdd, 0xa8, 0x1d, 0x1e, 0x2c, - 0x94, 0x57, 0x29, 0xb0, 0x79, 0x5b, 0x2f, 0xb3, 0xcf, 0xcd, 0xdd, 0x0c, 0x03, 0xdc, 0x85, 0xb2, - 0xd4, 0xef, 0xe2, 0xc5, 0xea, 0xd2, 0x4b, 0x99, 0xfd, 0x4e, 0x6b, 0xb3, 0x28, 0x3a, 0xbb, 0xe6, - 0x46, 0xc1, 0xbe, 0x1e, 0xb3, 0x68, 0x5c, 0x87, 0xe9, 0xd4, 0x27, 0x22, 0x51, 0x78, 0x6e, 0x45, - 0x27, 0x3f, 0x49, 0xbf, 0xf7, 0x0c, 0xa7, 0x8b, 0xa9, 0xaa, 0x15, 0x9d, 0x35, 0x3e, 0x56, 0xf8, - 0xa8, 0xa2, 0xd5, 0x41, 0xed, 0x35, 0xef, 0x9b, 0x13, 0x65, 0x45, 0x2d, 0x68, 0x5f, 0x51, 0x40, - 0x5d, 0x73, 0xcd, 0x60, 0xdf, 0x8f, 0xb0, 0xc5, 0x55, 0x41, 0xcf, 0x40, 0xc5, 0x77, 0x0c, 0xdb, - 0x8d, 0xf0, 0xa3, 0x28, 0x9e, 0x1a, 0x02, 0x90, 0x3d, 0xb2, 0x85, 0xc7, 0x1b, 0x59, 0x1f, 0x66, - 0xb9, 0xf0, 0x78, 0x6c, 0x2f, 0xc0, 0x2c, 0xf7, 0x76, 0x3a, 0x3d, 0x70, 0x10, 0x72, 0x55, 0x66, - 0x3a, 0x7d, 0xe3, 0xc7, 0x21, 0xc2, 0x2b, 0x79, 0x33, 0x71, 0x8f, 0xa2, 0xe4, 0x1e, 0x6f, 0x4e, - 0x94, 0x27, 0xd4, 0x49, 0xed, 0x4b, 0x0a, 0xd4, 0xa8, 0x93, 0xaf, 0x78, 0xac, 0x5b, 0x27, 0xa0, - 0x60, 0x5b, 0x4c, 0xc4, 0x8d, 0xa9, 0xc3, 0x83, 0x85, 0xc2, 0xfa, 0xaa, 0x5e, 0xb0, 0x2d, 0xf4, - 0x02, 0x80, 0x6f, 0x04, 0x64, 0xd2, 0x90, 0xe9, 0x59, 0xa0, 0xd3, 0x73, 0xfa, 0xf0, 0x60, 0xa1, - 0xd2, 0xa4, 0x50, 0x32, 0x2d, 0x2b, 0x0c, 0x61, 0xdd, 0x0a, 0xd1, 0x79, 0x28, 0xb3, 0x25, 0xd0, - 0xdf, 0x65, 0x52, 0x6f, 0x54, 0x0f, 0x0f, 0x16, 0x4a, 0xd4, 0x6d, 0x9b, 0xb7, 0xf5, 0x12, 0xfd, - 0xd8, 0xdc, 0xe5, 0x4a, 0xe8, 0x50, 0x5d, 0xf6, 0x93, 0xe9, 0x99, 0xf2, 0x37, 0x65, 0xa0, 0xbf, - 0xe5, 0x76, 0x5a, 0x6b, 0x03, 0x22, 0x5d, 0x32, 0xcc, 0x68, 0xd9, 0xb2, 0x96, 0xc9, 0xbe, 0x41, - 0xd6, 0xb3, 0x31, 0x58, 0x9f, 0x87, 0x32, 0xdf, 0x87, 0x84, 0xd3, 0xd3, 0x2e, 0x50, 0x56, 0xa4, - 0x0b, 0x6c, 0x2f, 0xda, 0xd5, 0x7e, 0x47, 0x81, 0x79, 0xda, 0xaf, 0x65, 0xcb, 0xba, 0x8b, 0x3b, - 0x5b, 0x38, 0x60, 0xcc, 0x88, 0xac, 0x0e, 0x6d, 0xf7, 0xc8, 0x62, 0x48, 0x44, 0x16, 0xfb, 0xdc, - 0xdc, 0x1d, 0x67, 0x86, 0x3d, 0x0b, 0xc0, 0xb9, 0x4a, 0x7b, 0x0f, 0x83, 0x90, 0x45, 0x7e, 0x0d, - 0x6a, 0x8c, 0x68, 0x83, 0x6d, 0x55, 0x4f, 0x43, 0xc5, 0xdc, 0x31, 0x6c, 0x57, 0x5a, 0xde, 0xcb, - 0x14, 0x40, 0xac, 0x21, 0x4d, 0xf9, 0x42, 0x6a, 0xca, 0x6b, 0xdf, 0x91, 0x3a, 0x95, 0xe2, 0x37, - 0x86, 0x01, 0x3f, 0x02, 0x33, 0x16, 0x0e, 0xa3, 0x56, 0x62, 0x04, 0xd6, 0x33, 0xf5, 0xf0, 0x60, - 0xa1, 0xb6, 0x8a, 0xc3, 0x28, 0x36, 0x44, 0xcd, 0x4a, 0x5a, 0xbb, 0xf2, 0xf2, 0x5a, 0x4c, 0x2d, - 0xaf, 0xda, 0x77, 0x15, 0x38, 0x73, 0xb7, 0xeb, 0x44, 0x36, 0xc3, 0x15, 0x0a, 0xd2, 0x21, 0xd1, - 0x71, 0xe8, 0x39, 0x7b, 0xbd, 0xab, 0xd5, 0x60, 0x0d, 0xcf, 0xc1, 0x0c, 0x1b, 0xe2, 0x80, 0x13, - 0x73, 0x27, 0x9a, 0x36, 0x52, 0x1c, 0x17, 0xa0, 0x2a, 0x22, 0x12, 0xcf, 0xdb, 0xe6, 0x4a, 0x01, - 0x8f, 0x45, 0x3c, 0x6f, 0x5b, 0xfb, 0x9a, 0x02, 0xa7, 0x52, 0x7a, 0x19, 0x6e, 0xb4, 0x6c, 0x75, - 0x6c, 0x57, 0xf7, 0x1c, 0x3c, 0x8e, 0x42, 0x9f, 0x80, 0xb9, 0x36, 0x21, 0xc6, 0xb8, 0xcf, 0x6a, - 0xc7, 0x0e, 0x0f, 0x16, 0x66, 0x6f, 0xb1, 0x8f, 0xb1, 0xe1, 0x66, 0xdb, 0x29, 0xc0, 0xae, 0xb6, - 0x06, 0x75, 0x49, 0x91, 0x75, 0xd7, 0x8e, 0x6c, 0xc3, 0x61, 0x8d, 0x31, 0xfc, 0x51, 0x33, 0xe0, - 0x4c, 0x6c, 0x5c, 0xcb, 0xb2, 0x23, 0xdb, 0x73, 0x0d, 0x27, 0x1d, 0x45, 0x8d, 0xd3, 0x2d, 0x04, - 0x13, 0x34, 0x28, 0x63, 0xd6, 0xa5, 0xbf, 0x35, 0x0b, 0xce, 0xb2, 0x30, 0x11, 0x77, 0xbc, 0x3d, - 0xfc, 0x61, 0x49, 0x79, 0x0f, 0x10, 0x8f, 0x5c, 0xa9, 0xb0, 0x37, 0x3d, 0xdb, 0x1d, 0x8f, 0x69, - 0x1c, 0xef, 0x16, 0x46, 0x8c, 0x77, 0x35, 0x0c, 0xaa, 0x2c, 0xf2, 0x0e, 0xde, 0x8e, 0xc6, 0x5c, - 0x76, 0xe2, 0x95, 0xb3, 0x90, 0xbf, 0x72, 0x6a, 0x6f, 0xc2, 0xb3, 0x5c, 0x0c, 0x5f, 0xe6, 0x74, - 0xfc, 0x5e, 0x17, 0x87, 0xd1, 0xaa, 0x1d, 0x1a, 0x5b, 0xce, 0x58, 0x9d, 0xd4, 0xd6, 0xe1, 0x99, - 0x4c, 0x5e, 0x6b, 0xee, 0xd8, 0xac, 0xbe, 0xaa, 0xc0, 0xd9, 0x4c, 0x5e, 0x3a, 0xde, 0xc6, 0x01, - 0x76, 0x4d, 0xac, 0xe3, 0x70, 0xbc, 0x75, 0x24, 0x3f, 0xc8, 0x2f, 0x0c, 0x08, 0xf2, 0x7f, 0xa6, - 0xe4, 0x18, 0x68, 0xcd, 0x7d, 0xaf, 0x8b, 0xbb, 0xe3, 0x79, 0xc1, 0x88, 0x83, 0x82, 0x3e, 0x41, - 0x16, 0x54, 0x2a, 0x8c, 0xae, 0x12, 0x79, 0x91, 0xc0, 0xc6, 0x8e, 0x11, 0x60, 0x62, 0x5a, 0xa1, - 0x99, 0xa0, 0x42, 0xcf, 0x41, 0xcd, 0x7b, 0xe8, 0xa6, 0x23, 0xc5, 0x9a, 0x5e, 0xf5, 0x1e, 0xba, - 0x71, 0x8c, 0x10, 0xc1, 0xa9, 0xcc, 0x7e, 0x6d, 0x60, 0x77, 0x2c, 0xb3, 0xbe, 0x00, 0xc0, 0xa5, - 0x26, 0xbd, 0xa2, 0x1b, 0x3a, 0x67, 0xdb, 0xbc, 0xad, 0x57, 0x38, 0x42, 0x73, 0x57, 0xfb, 0xc7, - 0x3c, 0x73, 0xea, 0xd8, 0xc4, 0xf6, 0xde, 0x78, 0xe6, 0x1c, 0x4b, 0x34, 0xfa, 0x08, 0x9c, 0x14, - 0xd8, 0xbd, 0x0e, 0xc0, 0x96, 0xe2, 0xe3, 0xa6, 0xd0, 0xa8, 0x67, 0xe9, 0x50, 0x05, 0x5d, 0x8f, - 0x3d, 0x67, 0x39, 0x3c, 0xb6, 0xe9, 0x3e, 0x9c, 0xce, 0x9b, 0x4c, 0xa6, 0x11, 0x58, 0x1f, 0x62, - 0xef, 0xb4, 0x3f, 0xc9, 0x33, 0xec, 0xb2, 0x69, 0x62, 0x12, 0x8d, 0x7e, 0x78, 0x86, 0x1d, 0x31, - 0x48, 0xd3, 0x7c, 0x38, 0x9e, 0xd6, 0xf0, 0x86, 0xe3, 0x99, 0xbb, 0x1f, 0xa6, 0x51, 0x02, 0x38, - 0x99, 0x96, 0xb8, 0xe9, 0x6e, 0x7d, 0xd8, 0x32, 0x7f, 0x57, 0x81, 0x3a, 0x17, 0xba, 0x81, 0x03, - 0xc2, 0xe2, 0x81, 0xb7, 0x8b, 0xdd, 0x65, 0x6b, 0xcc, 0xe1, 0xbf, 0x09, 0xd3, 0x21, 0xa3, 0x6f, - 0x45, 0x84, 0x01, 0xdf, 0x39, 0x9e, 0xcb, 0x5e, 0x09, 0x24, 0x49, 0x7a, 0x2d, 0x94, 0x5a, 0x9a, - 0x07, 0x8d, 0x0c, 0x75, 0xd8, 0x76, 0x39, 0xee, 0xe2, 0x45, 0x15, 0x69, 0xd9, 0x6c, 0xc5, 0xac, - 0xb0, 0x61, 0xa6, 0xec, 0xd6, 0x57, 0xf5, 0x12, 0xfd, 0xb8, 0x6e, 0x69, 0x1f, 0x88, 0xfc, 0x80, - 0x8e, 0x7d, 0xc7, 0x36, 0x8d, 0xc8, 0x76, 0xdb, 0xe3, 0xc8, 0x59, 0x05, 0x64, 0x74, 0xa3, 0x1d, - 0xec, 0x46, 0x94, 0xd8, 0x73, 0x5b, 0xdd, 0xc0, 0xe1, 0x12, 0xe9, 0x41, 0x7e, 0x39, 0xf5, 0x75, - 0x53, 0xbf, 0xa3, 0xcf, 0xa5, 0x09, 0x36, 0x03, 0x07, 0xbd, 0x08, 0x28, 0x10, 0xf2, 0x3d, 0xb7, - 0x45, 0x4c, 0x82, 0x03, 0xea, 0x9e, 0x15, 0x7d, 0x4e, 0xfa, 0xb2, 0x41, 0x3f, 0x68, 0x77, 0x60, - 0x8e, 0x9b, 0x87, 0x25, 0x34, 0x56, 0xc9, 0x21, 0xb1, 0x02, 0x25, 0x3e, 0x89, 0x1a, 0x2f, 0xc0, - 0x24, 0xe9, 0xce, 0x3e, 0x3a, 0x0b, 0xd3, 0x98, 0x62, 0x60, 0xab, 0x45, 0x97, 0x02, 0x16, 0x0c, - 0xd7, 0x04, 0x90, 0x10, 0x6a, 0xbf, 0x9a, 0x82, 0x93, 0x9c, 0xdd, 0x2d, 0x4c, 0x7c, 0x6f, 0xdb, - 0x6e, 0x77, 0x03, 0x2a, 0x4f, 0x66, 0xfa, 0xfe, 0x94, 0xe0, 0xfa, 0x02, 0x40, 0x9c, 0xdc, 0x12, - 0xf6, 0xa1, 0x2e, 0xc6, 0x87, 0x8e, 0xb8, 0x98, 0x48, 0x71, 0x8d, 0x15, 0xe6, 0x7f, 0x1c, 0x54, - 0xc1, 0xb8, 0x67, 0x8e, 0xa2, 0xc3, 0x83, 0x85, 0x19, 0x39, 0xc2, 0x68, 0xde, 0xd6, 0x67, 0x0c, - 0xb9, 0xbd, 0x8b, 0xce, 0x42, 0xc9, 0xc7, 0x38, 0x20, 0x23, 0x3e, 0x41, 0xed, 0x0f, 0x87, 0x07, - 0x0b, 0x53, 0x4d, 0x8c, 0x83, 0xf5, 0x55, 0x7d, 0x8a, 0x7c, 0x5a, 0xb7, 0xc8, 0xf1, 0xd6, 0xb1, - 0xc3, 0x08, 0xbb, 0xe4, 0x4c, 0x39, 0x79, 0xa6, 0x78, 0xb1, 0xa2, 0x27, 0x00, 0xf4, 0x19, 0xa8, - 0x6e, 0x39, 0xb8, 0x85, 0x59, 0x08, 0x40, 0xb3, 0x4d, 0x33, 0x4b, 0xaf, 0x0d, 0x72, 0xe2, 0x5e, - 0x8b, 0x2d, 0x6e, 0xe0, 0x88, 0xf8, 0xd0, 0x46, 0x64, 0x44, 0x58, 0x87, 0x2d, 0x07, 0x8b, 0x78, - 0xc2, 0x04, 0xf5, 0xa1, 0xbd, 0x6d, 0xb7, 0xfc, 0x25, 0x3f, 0x16, 0x50, 0x7a, 0x5c, 0x01, 0x33, - 0x84, 0x65, 0x73, 0xc9, 0x17, 0x42, 0xde, 0x85, 0x5a, 0xc7, 0x72, 0xc3, 0x58, 0x40, 0xf9, 0x71, - 0x05, 0x54, 0x09, 0x3b, 0xc1, 0xfd, 0xff, 0xc1, 0x74, 0x80, 0x1d, 0x63, 0x3f, 0x66, 0x5f, 0x79, - 0x5c, 0xf6, 0x35, 0xca, 0x4f, 0xf0, 0x7f, 0x00, 0x73, 0xc2, 0x55, 0xba, 0xe1, 0x0e, 0x5f, 0x49, - 0x80, 0xae, 0x24, 0x17, 0xb3, 0xb3, 0x0b, 0xdd, 0x70, 0x87, 0xcb, 0xe1, 0x5b, 0x72, 0xa0, 0xcf, - 0x72, 0x77, 0xea, 0x86, 0x3b, 0x74, 0xb6, 0xa3, 0xbb, 0x80, 0x64, 0xae, 0x7c, 0x72, 0x55, 0x29, - 0xdb, 0x85, 0x81, 0x6c, 0x71, 0xa0, 0xab, 0x09, 0x37, 0x3e, 0xf9, 0x6e, 0x41, 0x4d, 0xee, 0x02, - 0xaa, 0x42, 0x69, 0xd3, 0xdd, 0x75, 0xbd, 0x87, 0xae, 0xfa, 0x14, 0x69, 0xf0, 0xce, 0xa8, 0x0a, - 0xaa, 0x41, 0x59, 0x04, 0xa6, 0x6a, 0x01, 0xcd, 0x42, 0x75, 0xd3, 0x35, 0xf6, 0x0c, 0xdb, 0x21, - 0x10, 0xb5, 0xa8, 0x7d, 0x01, 0x4e, 0xe6, 0x44, 0x8b, 0xf2, 0xb4, 0x7b, 0x5b, 0xcc, 0xba, 0xfc, - 0x88, 0x50, 0xc9, 0x8f, 0x08, 0xc9, 0xb9, 0x52, 0x0c, 0x16, 0x99, 0x7b, 0x65, 0x5d, 0x34, 0xb5, - 0x2b, 0x70, 0x3c, 0x33, 0x88, 0x96, 0x85, 0x97, 0xb8, 0x70, 0xed, 0xb3, 0x30, 0x9f, 0x15, 0x25, - 0xcb, 0xb8, 0xaf, 0x3f, 0x96, 0xa2, 0xda, 0x0e, 0x3c, 0xd3, 0x6b, 0x8d, 0x10, 0x67, 0x9b, 0xe4, - 0x31, 0x25, 0x7d, 0x43, 0x89, 0xb3, 0x24, 0x49, 0x14, 0x69, 0x35, 0x3a, 0xb1, 0x00, 0x39, 0xa2, - 0x55, 0x9e, 0x48, 0x44, 0x5b, 0xe8, 0x8b, 0x68, 0x13, 0xd3, 0x7e, 0xba, 0xd7, 0xb4, 0x2c, 0x06, - 0x6a, 0xbc, 0x9a, 0xe8, 0x93, 0xde, 0xd3, 0x95, 0xc1, 0x7b, 0x7a, 0xc2, 0xf9, 0x9d, 0x8c, 0x11, - 0x26, 0x91, 0xdd, 0x13, 0x60, 0xdd, 0x84, 0x9a, 0x1c, 0x16, 0x3d, 0x01, 0x8e, 0x3a, 0xcc, 0xa4, - 0xc3, 0x9e, 0x27, 0xc0, 0xf3, 0x2d, 0x38, 0x26, 0xd2, 0x61, 0x3c, 0x17, 0x46, 0x47, 0xfa, 0xa5, - 0x84, 0xb1, 0x1c, 0x0d, 0x2a, 0xf9, 0xd1, 0x60, 0xc2, 0xf2, 0x01, 0x9c, 0xe8, 0x4d, 0xc6, 0xac, - 0x04, 0xd8, 0x88, 0x52, 0x0e, 0x7a, 0x55, 0x38, 0xe8, 0x88, 0xec, 0xb5, 0x77, 0x61, 0xbe, 0x97, - 0x2b, 0x39, 0xb5, 0x37, 0xae, 0x27, 0x9a, 0x8e, 0x7d, 0x07, 0x95, 0xe8, 0xbc, 0x01, 0xc7, 0x7b, - 0xb9, 0xdf, 0xc1, 0xc6, 0x1e, 0x7e, 0x2c, 0x43, 0x98, 0x70, 0xae, 0x2f, 0x2b, 0x25, 0x27, 0x90, - 0x88, 0xaf, 0x39, 0x5e, 0xf8, 0x78, 0x42, 0xbe, 0xa6, 0xc0, 0xe9, 0xfe, 0xdc, 0x17, 0xcf, 0x31, - 0xd1, 0xbc, 0x50, 0xe3, 0xdd, 0xb1, 0xd9, 0xa7, 0x73, 0x42, 0x85, 0x41, 0x39, 0xa1, 0x44, 0x93, - 0x6f, 0x66, 0x64, 0xe1, 0xd6, 0xdd, 0x3d, 0x3b, 0xa2, 0x9b, 0x1a, 0x77, 0x81, 0x23, 0x74, 0xf5, - 0x35, 0xe1, 0x2a, 0x63, 0x8f, 0xaf, 0xf6, 0x75, 0x05, 0x66, 0xa5, 0x0c, 0x32, 0x75, 0xed, 0xb7, - 0xc6, 0xb7, 0x46, 0xee, 0xc5, 0x0e, 0xbb, 0x35, 0x69, 0x68, 0x42, 0xc3, 0x53, 0x50, 0x34, 0xe3, - 0x2c, 0x79, 0xe9, 0xf0, 0x60, 0xa1, 0xb8, 0xb2, 0xbe, 0xaa, 0x13, 0x18, 0x19, 0xa7, 0x19, 0xaa, - 0x0a, 0x4d, 0x43, 0xff, 0x4f, 0x6a, 0xf2, 0x81, 0x02, 0x28, 0x75, 0xef, 0x45, 0xf3, 0xfc, 0xe4, - 0x7c, 0xc2, 0x2e, 0xbf, 0x4c, 0x2f, 0xb9, 0xd9, 0xc8, 0x3b, 0x9f, 0xc8, 0x57, 0x03, 0x7a, 0x0d, - 0xcb, 0x17, 0x05, 0x6f, 0x48, 0x17, 0x3b, 0xec, 0x88, 0xa3, 0xe5, 0x0f, 0x54, 0x7c, 0xe7, 0x11, - 0xd3, 0x24, 0xd7, 0x53, 0x45, 0xe9, 0x7a, 0x4a, 0xfb, 0x2b, 0x05, 0xe6, 0x38, 0x05, 0xbb, 0x07, - 0x79, 0xa2, 0x3a, 0xbf, 0x0e, 0x25, 0x71, 0x89, 0xc2, 0x54, 0x3e, 0x3b, 0xc2, 0x5d, 0x94, 0x2e, - 0x68, 0xe4, 0xdb, 0x86, 0x62, 0xfa, 0xb6, 0xe1, 0xbf, 0x12, 0xb5, 0x59, 0xf7, 0xee, 0xd8, 0x61, - 0xd4, 0xf8, 0xb9, 0x32, 0xfe, 0xc8, 0x9f, 0x87, 0x72, 0x68, 0xbb, 0x26, 0x16, 0xa7, 0x35, 0x8e, - 0xb7, 0x41, 0x60, 0xe4, 0xb4, 0x46, 0x3f, 0xae, 0x5b, 0xe8, 0x69, 0xa8, 0x30, 0x3c, 0xd7, 0x7b, - 0x48, 0xb5, 0x29, 0xeb, 0x8c, 0xf0, 0x9e, 0xf7, 0x90, 0x30, 0xe9, 0xba, 0x91, 0xed, 0x88, 0x03, - 0x00, 0x67, 0xb2, 0x49, 0x60, 0x84, 0x09, 0xfd, 0xc8, 0x98, 0x30, 0x3c, 0xc2, 0x64, 0x92, 0x31, - 0xa1, 0x00, 0xc2, 0xe4, 0x2c, 0x09, 0x71, 0xf7, 0x70, 0x10, 0xe2, 0x96, 0x17, 0x58, 0x38, 0xa0, - 0x67, 0x80, 0x32, 0x89, 0x53, 0x29, 0xf0, 0x3e, 0x81, 0x25, 0x97, 0xca, 0xdc, 0x66, 0xff, 0x5b, - 0xfa, 0xfd, 0x1b, 0x05, 0x2a, 0x7c, 0xe5, 0xdb, 0xf6, 0x1a, 0xad, 0xf1, 0xfb, 0x3b, 0x56, 0x72, - 0xa2, 0xf1, 0xfb, 0xca, 0x91, 0x17, 0xc7, 0x31, 0xd6, 0xf8, 0xf4, 0x01, 0xb5, 0x38, 0x30, 0xd1, - 0xfb, 0x39, 0x98, 0x5e, 0x36, 0x23, 0x5a, 0x89, 0x41, 0xa5, 0x35, 0x9a, 0xe3, 0xdb, 0xe0, 0x59, - 0x00, 0xc7, 0x33, 0x0d, 0xa7, 0xe5, 0xb9, 0xce, 0x3e, 0x0f, 0xca, 0x2b, 0x14, 0x72, 0xdf, 0x75, - 0xf6, 0x93, 0x1d, 0xe7, 0x2e, 0xcc, 0xae, 0x62, 0x23, 0x25, 0xed, 0x71, 0xb6, 0xd2, 0x6f, 0x4e, - 0xf2, 0xc9, 0xca, 0x6f, 0xb6, 0x22, 0x23, 0xea, 0x86, 0x47, 0xe1, 0xf8, 0xfd, 0xa2, 0x18, 0x95, - 0x4f, 0xc0, 0x84, 0x54, 0x4b, 0x70, 0x25, 0x7f, 0x50, 0x64, 0x91, 0x8b, 0xb4, 0xb0, 0x80, 0x12, - 0x66, 0x5f, 0xd6, 0x37, 0x7e, 0xa4, 0xc0, 0x34, 0x39, 0xb5, 0xaf, 0x78, 0xae, 0x8b, 0xcd, 0x08, - 0x5b, 0xf2, 0xc9, 0x5e, 0xc9, 0x3d, 0xd9, 0x8f, 0x91, 0x67, 0x68, 0x02, 0x44, 0x81, 0xe1, 0x86, - 0xbe, 0x17, 0x44, 0xac, 0x78, 0x63, 0x66, 0xe9, 0xda, 0xa8, 0xea, 0x0b, 0x42, 0x5d, 0xe2, 0x81, - 0x4e, 0xc0, 0x54, 0xc7, 0xb0, 0xac, 0x80, 0xd5, 0x70, 0x54, 0x74, 0xde, 0x6a, 0xbc, 0x0a, 0x2a, - 0x51, 0x53, 0xc7, 0x26, 0xeb, 0x8c, 0xed, 0xb6, 0x47, 0xea, 0x8d, 0x20, 0x24, 0x51, 0xd4, 0x58, - 0x66, 0xd0, 0xb6, 0x60, 0x82, 0xd6, 0x6b, 0xcc, 0x42, 0x95, 0xfc, 0x9b, 0x1c, 0x4d, 0xeb, 0x30, - 0x4f, 0x00, 0xbd, 0x5c, 0x55, 0x05, 0x1d, 0x87, 0x39, 0xf1, 0x25, 0xb6, 0xb9, 0x5a, 0x90, 0x09, - 0x64, 0xfd, 0xd5, 0xa2, 0xb6, 0x06, 0x95, 0xd8, 0x0c, 0x68, 0x06, 0xe0, 0x81, 0x1f, 0x25, 0x72, - 0x00, 0xa6, 0x1e, 0xf8, 0xd1, 0x9d, 0xe5, 0x7b, 0xaa, 0xc2, 0x7f, 0xbf, 0xbd, 0x7c, 0x4f, 0x2d, - 0x20, 0x15, 0x6a, 0x0f, 0xfc, 0xa8, 0x19, 0x78, 0x8f, 0xec, 0x8e, 0x1d, 0xed, 0xab, 0x45, 0xed, - 0xef, 0x15, 0xe2, 0xe2, 0x5b, 0xdd, 0x36, 0x59, 0x3f, 0xa9, 0xa5, 0x43, 0x39, 0x8a, 0xfe, 0x33, - 0x65, 0xcc, 0x30, 0x1a, 0xdd, 0x49, 0xd5, 0x20, 0x15, 0x46, 0xa9, 0x41, 0x62, 0xcb, 0x4f, 0x66, - 0x49, 0x52, 0x7a, 0xb1, 0x2a, 0x0e, 0xc9, 0xa4, 0xfe, 0x5e, 0x11, 0x4e, 0xd0, 0xce, 0xac, 0xbb, - 0xa1, 0x8f, 0x4d, 0xd6, 0x9f, 0x8d, 0xc8, 0x0b, 0x70, 0xe3, 0xeb, 0x47, 0xd8, 0x19, 0x36, 0xa1, - 0xec, 0x78, 0x6d, 0xb9, 0x23, 0x2f, 0x66, 0x76, 0xa4, 0x4f, 0xe4, 0x1d, 0xaf, 0x4d, 0xfb, 0x45, - 0xd9, 0xf2, 0x86, 0x5e, 0x72, 0xd8, 0x8f, 0xc6, 0x2f, 0x94, 0xe1, 0x31, 0x14, 0xba, 0x0a, 0x55, - 0x5e, 0xf5, 0x60, 0x26, 0x65, 0x0f, 0x33, 0x87, 0x07, 0x0b, 0xc0, 0xca, 0x1e, 0x68, 0x39, 0x12, - 0x2f, 0x8c, 0xa0, 0x75, 0x48, 0xf7, 0xa4, 0xaa, 0x28, 0xa9, 0xc6, 0xa8, 0x38, 0x52, 0x8d, 0x51, - 0x5c, 0x1e, 0x15, 0x83, 0xd2, 0x53, 0x79, 0x62, 0x58, 0x2d, 0x84, 0x88, 0x19, 0xa7, 0xd2, 0xf7, - 0xe6, 0x3e, 0x00, 0x35, 0xce, 0x91, 0x97, 0x4e, 0xf9, 0x14, 0xc7, 0xe7, 0x5d, 0x58, 0x57, 0xc8, - 0xf4, 0x66, 0x04, 0x6c, 0xe2, 0x85, 0x7a, 0x89, 0xcd, 0xbc, 0x50, 0xfb, 0xd3, 0x02, 0xcc, 0x2f, - 0x77, 0xa3, 0x9d, 0xb5, 0x47, 0xe6, 0x8e, 0xe1, 0xb6, 0xb1, 0x8e, 0x43, 0xdf, 0x73, 0x43, 0x8c, - 0x9e, 0x83, 0x9a, 0x61, 0x9a, 0x38, 0x0c, 0x79, 0x4a, 0x8b, 0xd5, 0xed, 0x54, 0x19, 0x8c, 0x25, - 0xa9, 0xe6, 0x61, 0x32, 0x34, 0x3d, 0x3f, 0xae, 0xdf, 0xa1, 0x0d, 0xba, 0x40, 0x06, 0x81, 0x27, - 0x52, 0xc1, 0xac, 0x81, 0xae, 0xc0, 0x1c, 0xfd, 0xd1, 0xb2, 0x70, 0x68, 0x06, 0xb6, 0x4f, 0x4e, - 0x20, 0x2c, 0xe5, 0xa9, 0xab, 0xf4, 0xc3, 0x6a, 0x02, 0x47, 0x1b, 0x50, 0xe6, 0x19, 0x76, 0x96, - 0xef, 0xac, 0x2e, 0xbd, 0x9a, 0x39, 0x20, 0x59, 0x8a, 0x8b, 0x1c, 0x5e, 0xc8, 0x0b, 0x92, 0x04, - 0xa3, 0xc6, 0x75, 0x98, 0x4e, 0x7d, 0x1a, 0xab, 0x20, 0xe9, 0x87, 0x0a, 0xd4, 0xe9, 0xc8, 0x10, - 0x91, 0x9c, 0xcd, 0x06, 0x8e, 0xa8, 0x1d, 0x1a, 0xdf, 0x54, 0xe4, 0x34, 0xcc, 0x64, 0x62, 0xaf, - 0xea, 0xd2, 0xa5, 0x91, 0xf5, 0xd6, 0x19, 0xdd, 0x93, 0x49, 0xce, 0x27, 0x5b, 0xe8, 0xff, 0x07, - 0xb5, 0x37, 0xe5, 0x83, 0x4e, 0x40, 0x21, 0x76, 0x23, 0x5a, 0x39, 0xd4, 0xbc, 0xad, 0x17, 0xfc, - 0x23, 0xde, 0xdf, 0xa2, 0x86, 0x74, 0xbc, 0x60, 0xc1, 0x76, 0xdc, 0xd6, 0x1c, 0x78, 0x46, 0xbe, - 0x13, 0xd9, 0xe8, 0xfa, 0x2c, 0x87, 0xcf, 0x81, 0xc4, 0xc9, 0xe2, 0x2b, 0x18, 0xb1, 0x2b, 0x57, - 0xf4, 0xaa, 0xb8, 0x5e, 0x61, 0xf3, 0x4a, 0x15, 0x28, 0xd8, 0xb5, 0x7c, 0xcf, 0xe6, 0x5b, 0x6f, - 0x45, 0x9f, 0xe5, 0xf0, 0x35, 0x0e, 0xd6, 0xfe, 0x55, 0x81, 0x9a, 0x2c, 0x8e, 0x8c, 0xa7, 0xec, - 0xbc, 0x4f, 0xd2, 0xc2, 0xe8, 0xb3, 0x80, 0x42, 0xd1, 0x9d, 0x56, 0xec, 0xad, 0xc5, 0x01, 0x85, - 0x73, 0x83, 0x2c, 0xa1, 0xcf, 0x85, 0x3d, 0x90, 0x10, 0x9d, 0x06, 0xc0, 0x8f, 0x7c, 0x9b, 0xa5, - 0xa0, 0xe9, 0x5c, 0x29, 0xea, 0x12, 0x44, 0xfb, 0x6d, 0x05, 0x4e, 0x4a, 0xee, 0xb8, 0xe2, 0x75, - 0x7c, 0x07, 0x47, 0xf8, 0xa6, 0xe3, 0x3d, 0x6c, 0xbc, 0x9e, 0x78, 0xe4, 0x12, 0xd4, 0x4c, 0xc3, - 0x71, 0xb6, 0x0c, 0x73, 0x97, 0x76, 0x94, 0x6d, 0xc3, 0xb3, 0x87, 0x07, 0x0b, 0xd5, 0x15, 0x0e, - 0x27, 0x5d, 0xac, 0x0a, 0x24, 0xe2, 0x3e, 0xf2, 0x32, 0x12, 0x5f, 0x49, 0x29, 0x03, 0xae, 0xa4, - 0x7e, 0xa8, 0xc0, 0x31, 0x49, 0x97, 0x75, 0xd7, 0x8e, 0xa8, 0x1e, 0x77, 0x53, 0x4b, 0x18, 0xb1, - 0xa2, 0xa4, 0x03, 0x2b, 0xd3, 0xea, 0x46, 0x3b, 0x44, 0x7e, 0x89, 0x7c, 0x24, 0x86, 0x6d, 0x48, - 0x93, 0xbf, 0x48, 0x03, 0x93, 0x64, 0x0e, 0x37, 0xa5, 0x9d, 0x20, 0xe1, 0x43, 0x77, 0x02, 0xc2, - 0x83, 0xc0, 0xc8, 0x16, 0x18, 0x62, 0xb3, 0x1b, 0xe0, 0x78, 0x58, 0xcb, 0x6c, 0x0b, 0xdc, 0xa0, - 0x50, 0x82, 0x57, 0x61, 0x08, 0x9b, 0x81, 0xa3, 0xfd, 0x42, 0x81, 0x73, 0x2b, 0x01, 0xb6, 0xc8, - 0xe0, 0x1a, 0xce, 0xa7, 0x70, 0x60, 0x6f, 0x4b, 0xf7, 0x56, 0x72, 0x57, 0xa4, 0x5c, 0xeb, 0x55, - 0x10, 0x2e, 0x2a, 0xf5, 0x86, 0x6e, 0x36, 0x9c, 0x88, 0x08, 0x01, 0x8e, 0x42, 0xfa, 0x94, 0x2e, - 0xde, 0x2d, 0xf4, 0x16, 0xef, 0x22, 0x98, 0x70, 0x6c, 0x77, 0x97, 0xaf, 0x98, 0xf4, 0xf7, 0x87, - 0xd0, 0xd5, 0xef, 0x28, 0x70, 0x69, 0x60, 0x57, 0x47, 0xf3, 0x20, 0x3b, 0xdb, 0x83, 0xd6, 0x65, - 0x0f, 0xb2, 0x1b, 0x17, 0x84, 0xfa, 0xa7, 0x01, 0x6c, 0x2a, 0x72, 0xdb, 0xe6, 0x05, 0xab, 0x15, - 0x5d, 0x82, 0x68, 0x5f, 0x2e, 0xc0, 0x49, 0xa6, 0x0b, 0xb6, 0x12, 0xed, 0x42, 0x7a, 0x3c, 0xfd, - 0x8a, 0xb4, 0xb0, 0x5e, 0x81, 0xb9, 0x6d, 0xdb, 0x89, 0xe8, 0x96, 0xd6, 0xc3, 0x4e, 0x65, 0x1f, - 0xd6, 0x63, 0x38, 0x39, 0x19, 0x0a, 0xe4, 0x30, 0xec, 0xf2, 0x72, 0xb1, 0x8a, 0x5e, 0xe3, 0x88, - 0x14, 0x86, 0x2e, 0xc0, 0x2c, 0x7e, 0x64, 0x3a, 0x5d, 0x0b, 0xb7, 0xe8, 0xac, 0xe2, 0x65, 0x0a, - 0x65, 0x7d, 0x86, 0x83, 0xd7, 0x18, 0xb4, 0x61, 0x88, 0xbe, 0x7c, 0x1a, 0xc0, 0x8c, 0x55, 0xe4, - 0x2b, 0xfc, 0x47, 0xb3, 0x57, 0x78, 0x76, 0xcd, 0xd7, 0xdf, 0x31, 0x1d, 0xb7, 0xed, 0x30, 0xc2, - 0x01, 0xb6, 0x74, 0x89, 0x97, 0xf6, 0x0d, 0x25, 0xbe, 0x1e, 0x65, 0x9b, 0x2b, 0xed, 0xbf, 0x14, - 0x58, 0x3a, 0x63, 0xce, 0x48, 0x74, 0x1d, 0x4a, 0xdc, 0x01, 0x47, 0xbf, 0xd7, 0x16, 0x14, 0xda, - 0x6f, 0xf5, 0x68, 0xb3, 0xe2, 0x59, 0x38, 0x35, 0x31, 0x95, 0xf4, 0xc4, 0x44, 0xe7, 0x60, 0xc6, - 0xf4, 0x2c, 0xdc, 0x32, 0x77, 0x0c, 0xc7, 0xc1, 0x6e, 0x5b, 0x6c, 0xa1, 0xd3, 0x04, 0xba, 0x22, - 0x80, 0x29, 0xe5, 0x8b, 0x03, 0x96, 0x93, 0xf7, 0x15, 0x58, 0xd0, 0xd3, 0x57, 0xc8, 0xf4, 0xba, - 0x8c, 0xd9, 0x8e, 0x45, 0x47, 0xef, 0xa4, 0x96, 0x96, 0x91, 0x6c, 0x32, 0x62, 0x75, 0x50, 0xb2, - 0x7b, 0x7e, 0x51, 0x81, 0x33, 0x59, 0x7a, 0x30, 0x08, 0x3f, 0xe1, 0x3e, 0x56, 0xc2, 0x7b, 0x41, - 0x8c, 0xeb, 0x09, 0x28, 0x78, 0x6c, 0x53, 0x2e, 0xb3, 0x4d, 0xf9, 0xfe, 0x6d, 0xbd, 0xe0, 0xed, - 0x6a, 0x3f, 0x04, 0x80, 0x8d, 0xfd, 0x30, 0xc2, 0x1d, 0x9a, 0xc0, 0x90, 0x5c, 0xe2, 0xdf, 0xe3, - 0xb8, 0x78, 0x19, 0x4a, 0x7e, 0xe0, 0x91, 0xc0, 0x8c, 0x0b, 0xbe, 0x90, 0x3d, 0xd6, 0x31, 0x9b, - 0xc5, 0x26, 0x43, 0xd7, 0x05, 0x1d, 0x7a, 0x03, 0x8a, 0xfe, 0x92, 0x3f, 0x30, 0xd9, 0x26, 0x93, - 0x2f, 0x35, 0xd9, 0x52, 0xd4, 0x5c, 0x6a, 0xea, 0x84, 0x10, 0xdd, 0x83, 0x92, 0x17, 0x6c, 0xd9, - 0x91, 0xb5, 0xc5, 0x0b, 0xaa, 0x86, 0xaa, 0x70, 0x9f, 0xa0, 0xaf, 0xde, 0x60, 0x43, 0xc0, 0x1b, - 0xba, 0x60, 0x42, 0xb6, 0xee, 0x87, 0x46, 0xe0, 0x8a, 0xb3, 0x29, 0x6b, 0x34, 0xfe, 0x4d, 0x01, - 0x81, 0x8a, 0xac, 0xe4, 0xe2, 0x3d, 0x8e, 0x3f, 0x58, 0xef, 0x5f, 0x1b, 0x51, 0xf4, 0xa2, 0x3c, - 0xb4, 0xf4, 0xa4, 0xac, 0xcf, 0x72, 0x96, 0xf1, 0x95, 0xd7, 0x17, 0x60, 0xae, 0x0f, 0x8b, 0xcc, - 0x04, 0x3f, 0xf0, 0xda, 0x81, 0x30, 0x78, 0x51, 0x8f, 0xdb, 0x34, 0xf5, 0x68, 0x3c, 0xb2, 0x3b, - 0xdd, 0x0e, 0x35, 0x66, 0x51, 0x17, 0x4d, 0x42, 0xb5, 0xd5, 0xdd, 0xde, 0xc6, 0x62, 0xa1, 0x29, - 0xea, 0x71, 0x9b, 0x9c, 0xc5, 0x59, 0xb1, 0x1b, 0xdf, 0xe7, 0x79, 0xab, 0xb1, 0x08, 0xc4, 0xc4, - 0x64, 0xa9, 0x8a, 0x0f, 0xbf, 0x2d, 0x12, 0xba, 0x0b, 0xb9, 0x33, 0x31, 0x98, 0x44, 0xf6, 0x61, - 0xe3, 0xeb, 0x53, 0x50, 0xe2, 0x63, 0x4b, 0x34, 0xd9, 0xc3, 0x41, 0x48, 0x82, 0x07, 0xb6, 0x4e, - 0x8a, 0x26, 0x3a, 0x09, 0xa5, 0x3d, 0x33, 0x6c, 0x05, 0x78, 0x9b, 0x4f, 0xd3, 0xa9, 0x3d, 0x33, - 0xd4, 0xf1, 0x36, 0x39, 0xc4, 0x74, 0xfd, 0xc8, 0xee, 0xe0, 0x56, 0x27, 0x64, 0x3a, 0xb2, 0x43, - 0xcc, 0x26, 0x05, 0xde, 0xdd, 0xd0, 0xcb, 0xec, 0xf3, 0xdd, 0x10, 0x7d, 0x0c, 0xd4, 0x6e, 0x88, - 0x83, 0x96, 0xe9, 0x77, 0x5b, 0x82, 0x02, 0x28, 0xc5, 0xdc, 0xe1, 0xc1, 0xc2, 0xf4, 0x66, 0x88, - 0x83, 0x95, 0xe6, 0xe6, 0x03, 0x46, 0x36, 0x4d, 0x50, 0x57, 0xfc, 0xee, 0x03, 0x46, 0xfb, 0x49, - 0x40, 0x21, 0x1d, 0x8d, 0x14, 0x75, 0x95, 0x52, 0xd3, 0xf2, 0x59, 0x36, 0x56, 0x09, 0xfd, 0x2c, - 0x43, 0x4f, 0x38, 0x3c, 0x0b, 0x10, 0x46, 0x06, 0x8d, 0xbd, 0x8c, 0xa8, 0x5e, 0xa3, 0xb6, 0xa8, - 0x70, 0xc8, 0x32, 0x7d, 0x2c, 0x13, 0x38, 0xe4, 0xc8, 0xde, 0x32, 0xbb, 0x41, 0x7d, 0x9a, 0x96, - 0x4c, 0x57, 0x18, 0x64, 0xa5, 0x4b, 0xb7, 0x07, 0xb7, 0xdb, 0x69, 0xb5, 0xbd, 0xc0, 0xeb, 0x46, - 0xb6, 0x8b, 0xeb, 0x33, 0x94, 0x41, 0xcd, 0xed, 0x76, 0x6e, 0x09, 0x18, 0x19, 0x12, 0xd7, 0xdb, - 0xb6, 0x1d, 0x5c, 0x9f, 0x65, 0x43, 0xc2, 0x5a, 0xe8, 0x45, 0x38, 0x16, 0x79, 0x5e, 0xab, 0x63, - 0xb8, 0xfb, 0x2d, 0xcf, 0xc7, 0x6e, 0x8b, 0x40, 0xc3, 0xba, 0x4a, 0xb7, 0x0e, 0x35, 0xf2, 0xbc, - 0xbb, 0x86, 0xbb, 0x7f, 0xdf, 0xc7, 0xee, 0x4d, 0x02, 0x47, 0x67, 0xa1, 0x44, 0x64, 0x99, 0x7e, - 0xb7, 0x3e, 0x47, 0x3b, 0x48, 0x13, 0x20, 0xf7, 0xba, 0xa4, 0x77, 0xfa, 0x94, 0xdb, 0x25, 0x9d, - 0x22, 0xfa, 0xb6, 0xbd, 0x96, 0x18, 0x2d, 0x44, 0xc7, 0xa4, 0xd2, 0xf6, 0x3e, 0xc5, 0xc7, 0xeb, - 0x12, 0xa8, 0x9e, 0x8f, 0x03, 0x5a, 0xe8, 0xd3, 0x62, 0xa6, 0xa8, 0x1f, 0x63, 0x31, 0x70, 0x0c, - 0x67, 0x26, 0x43, 0x4f, 0x43, 0x65, 0xc7, 0x0b, 0xa3, 0x96, 0x6b, 0x74, 0x70, 0x7d, 0x9e, 0xe2, - 0x94, 0x09, 0xe0, 0x9e, 0xd1, 0xc1, 0x24, 0xce, 0x30, 0x02, 0x73, 0xa7, 0x7e, 0x9c, 0xc5, 0x19, - 0xe4, 0xb7, 0x64, 0xaa, 0x8e, 0xf1, 0xa8, 0x7e, 0x42, 0x36, 0xd5, 0x5d, 0xe3, 0x11, 0x89, 0x3e, - 0x7c, 0xdb, 0xaa, 0x9f, 0xa4, 0xaa, 0xb3, 0x29, 0x4f, 0x8e, 0xdc, 0xbe, 0x6d, 0xa1, 0x67, 0x60, - 0xc2, 0x27, 0xdf, 0xea, 0xf4, 0x5b, 0xf9, 0xf0, 0x60, 0x61, 0xa2, 0x49, 0x3e, 0x52, 0x28, 0x9b, - 0x23, 0xb6, 0x17, 0xd8, 0xd1, 0x7e, 0xfd, 0x94, 0x98, 0x23, 0xac, 0x4d, 0x43, 0x1a, 0xdb, 0xaa, - 0x37, 0x12, 0xa6, 0x9b, 0x84, 0x69, 0xd7, 0xb6, 0xd0, 0x02, 0x54, 0x1f, 0x7a, 0xc1, 0x2e, 0xe9, - 0xa8, 0x65, 0x07, 0xf5, 0xa7, 0x59, 0xbc, 0xc0, 0x41, 0xab, 0x36, 0xdd, 0xb5, 0xb9, 0xef, 0x10, - 0x9f, 0xa2, 0xdd, 0x7c, 0x86, 0x22, 0xcd, 0x30, 0xf0, 0x26, 0x87, 0x6a, 0xbf, 0x9e, 0x84, 0x32, - 0x99, 0x14, 0xbd, 0x3b, 0xe9, 0xb2, 0x58, 0x35, 0x3f, 0x0a, 0x93, 0x62, 0x2a, 0x15, 0x73, 0x2f, - 0x45, 0x04, 0x07, 0xfa, 0x43, 0x67, 0x04, 0x8d, 0x0f, 0x0a, 0x30, 0x41, 0xda, 0xd2, 0x1b, 0x8c, - 0x4a, 0xea, 0x0d, 0xc6, 0x75, 0x98, 0x22, 0x6e, 0x84, 0x59, 0x22, 0x22, 0x6f, 0x41, 0x8d, 0x79, - 0xeb, 0x04, 0x57, 0xe7, 0x24, 0xc4, 0xf1, 0xe8, 0x89, 0x58, 0x84, 0xbf, 0xbc, 0x85, 0x96, 0xa1, - 0xbc, 0x8d, 0x8d, 0xa8, 0x1b, 0x60, 0xb6, 0x2a, 0xce, 0xe4, 0x3d, 0x5f, 0x11, 0x6c, 0x6f, 0x32, - 0x6c, 0x3d, 0x26, 0x23, 0xd6, 0xed, 0xd8, 0x6e, 0xcb, 0x31, 0x22, 0xec, 0x9a, 0xec, 0xfd, 0x55, - 0x51, 0x87, 0x8e, 0xed, 0xde, 0x61, 0x10, 0xe2, 0x3e, 0x76, 0xd8, 0xa2, 0x19, 0x5c, 0xcc, 0xd3, - 0xe9, 0x65, 0x3b, 0xa4, 0xf9, 0x63, 0x8c, 0x3e, 0x0e, 0x15, 0xcb, 0x0e, 0xb0, 0x49, 0xcf, 0x23, - 0xa5, 0x01, 0x89, 0x92, 0x55, 0x81, 0xa5, 0x27, 0x04, 0x8d, 0x9f, 0x91, 0xed, 0x8a, 0xf4, 0x30, - 0x2d, 0x44, 0xe9, 0x11, 0x52, 0x87, 0x92, 0x61, 0x59, 0x74, 0x69, 0x65, 0x6b, 0x93, 0x68, 0xa6, - 0xc5, 0x17, 0xc7, 0x14, 0x4f, 0xf8, 0x8a, 0x6e, 0xb3, 0x25, 0x56, 0x34, 0xd1, 0x1b, 0x50, 0x0a, - 0xa3, 0x00, 0x1b, 0x1d, 0x91, 0x6c, 0x78, 0x7e, 0xb0, 0x59, 0x37, 0x28, 0xb2, 0x2e, 0x88, 0x1a, - 0x67, 0x60, 0x8a, 0x81, 0xf2, 0xdc, 0x41, 0x7b, 0x0f, 0x4a, 0x7c, 0x2c, 0x10, 0x82, 0x19, 0x9e, - 0x76, 0xe4, 0x10, 0xf5, 0x29, 0x34, 0x0b, 0xd5, 0xb7, 0x71, 0xb8, 0x23, 0x00, 0x0a, 0x9a, 0x01, - 0xb8, 0x71, 0x67, 0x4d, 0xb4, 0x69, 0x1a, 0xf2, 0x8e, 0x67, 0x1a, 0x8e, 0x80, 0x14, 0x69, 0x02, - 0xd3, 0x0b, 0x44, 0x7b, 0x82, 0xb0, 0x78, 0xab, 0x6b, 0x9b, 0x02, 0x30, 0xa9, 0x7d, 0x5f, 0x81, - 0x72, 0x53, 0xec, 0x49, 0xf3, 0x30, 0x19, 0x46, 0x46, 0x24, 0xce, 0xd7, 0xac, 0x41, 0xa0, 0x96, - 0x67, 0xbb, 0x6d, 0x91, 0xed, 0xa0, 0x8d, 0xd4, 0xde, 0x46, 0x8c, 0x5c, 0x90, 0xf6, 0xb6, 0x67, - 0xa0, 0x62, 0xf2, 0x33, 0x02, 0xdb, 0xa8, 0x26, 0xf4, 0x04, 0xc0, 0x4e, 0xdb, 0x91, 0xe1, 0x50, - 0xb7, 0x9a, 0xd0, 0x59, 0x83, 0x4a, 0xc1, 0x8e, 0xc1, 0x9e, 0x41, 0x4e, 0xe8, 0xac, 0xa1, 0xb9, - 0x30, 0xc7, 0x6e, 0x35, 0xde, 0xb6, 0xa3, 0x1d, 0x96, 0x22, 0x0b, 0xc7, 0x79, 0x87, 0xb3, 0x08, - 0x55, 0x96, 0x4e, 0x0b, 0x5b, 0xfe, 0x6e, 0xea, 0x95, 0x93, 0xc8, 0xb7, 0x85, 0x3a, 0x70, 0x8c, - 0xe6, 0x6e, 0xa8, 0x1d, 0x28, 0x30, 0x77, 0xbf, 0x1b, 0xdd, 0xdf, 0xa6, 0xd9, 0x4d, 0xf1, 0x6e, - 0x6c, 0x40, 0x3e, 0x71, 0x8c, 0xcc, 0xbc, 0xf4, 0x38, 0x87, 0x18, 0x6c, 0x2a, 0x79, 0x8f, 0xc7, - 0x1f, 0xd9, 0x4d, 0x24, 0x8f, 0xec, 0xe6, 0x61, 0x72, 0xdb, 0x31, 0xda, 0x21, 0xb5, 0x51, 0x49, - 0x67, 0x0d, 0x9a, 0x1c, 0x13, 0x6f, 0xda, 0x5a, 0xe9, 0xd4, 0xa0, 0x1a, 0x7f, 0x68, 0xf2, 0xa7, - 0x8b, 0xf1, 0x23, 0xb1, 0x92, 0xf4, 0x48, 0x4c, 0xfb, 0x89, 0x02, 0xc7, 0x32, 0x2a, 0xcb, 0xd0, - 0x2a, 0x00, 0x0b, 0x8d, 0xa5, 0x5b, 0x0f, 0x69, 0xd9, 0xe8, 0x86, 0x3b, 0x3d, 0x35, 0x69, 0x34, - 0x68, 0x66, 0x69, 0xe5, 0x48, 0xfc, 0x24, 0xd6, 0xd8, 0xea, 0xba, 0x96, 0x83, 0x93, 0xd2, 0x54, - 0x6a, 0x8d, 0x1b, 0x14, 0xb8, 0xbe, 0x4a, 0x22, 0x19, 0xfa, 0xcb, 0x4a, 0x72, 0x2e, 0xfc, 0xb6, - 0x98, 0xe5, 0x5c, 0xae, 0xc1, 0x7c, 0x80, 0x4d, 0xdb, 0xb7, 0xb1, 0x1b, 0xb5, 0xa4, 0xa3, 0x30, - 0x33, 0x0d, 0x8a, 0xbf, 0x35, 0xc5, 0x99, 0x58, 0xbb, 0x07, 0x90, 0x14, 0xb0, 0xb1, 0x87, 0xbc, - 0xe4, 0x97, 0xfc, 0xfa, 0x95, 0x41, 0xc8, 0x01, 0x5a, 0xca, 0x23, 0x91, 0xd5, 0x82, 0x7b, 0xb4, - 0x38, 0xa4, 0x2f, 0x5b, 0x56, 0xa0, 0x7d, 0x55, 0x81, 0x53, 0x84, 0x21, 0x1b, 0x40, 0x5e, 0xa2, - 0x2b, 0xce, 0x62, 0xe8, 0x8d, 0x74, 0xda, 0x6e, 0xf4, 0xca, 0x3d, 0xde, 0xbf, 0xd1, 0xdd, 0x85, - 0x9c, 0x29, 0x1a, 0x89, 0x22, 0xbc, 0x64, 0x2f, 0xd1, 0xe4, 0x55, 0x98, 0xe2, 0xd5, 0x7e, 0xca, - 0x68, 0xd5, 0x7e, 0x1c, 0x7d, 0x1c, 0x15, 0x7e, 0x5c, 0x88, 0xdf, 0x7e, 0x0c, 0x3a, 0xa1, 0x8e, - 0x53, 0x53, 0x7c, 0x1d, 0x1a, 0xa1, 0xdd, 0x76, 0xb1, 0xc5, 0x8f, 0xe7, 0xd1, 0x7e, 0xab, 0x2f, - 0xe3, 0x71, 0x92, 0x61, 0xac, 0x73, 0x84, 0x78, 0xac, 0xd1, 0x55, 0x38, 0xb6, 0xc7, 0xf5, 0x68, - 0x49, 0x07, 0x6c, 0x96, 0x0e, 0x41, 0x7b, 0x7d, 0x2a, 0x92, 0x09, 0x13, 0x50, 0x35, 0x59, 0x2a, - 0xac, 0x65, 0x91, 0xc5, 0x8d, 0x2d, 0xeb, 0xaa, 0xfc, 0x61, 0x95, 0xac, 0x73, 0xf4, 0x9c, 0x2f, - 0xb2, 0x66, 0x0c, 0x95, 0x6d, 0x7c, 0x33, 0x09, 0x98, 0x22, 0xa6, 0x53, 0x15, 0x53, 0xbd, 0xa9, - 0x0a, 0xb2, 0x31, 0xf3, 0x74, 0x42, 0x89, 0x45, 0xcd, 0xac, 0xa5, 0x7d, 0x4b, 0x81, 0xe3, 0x64, - 0x40, 0xd8, 0x3a, 0x45, 0x5d, 0x6b, 0xd3, 0x27, 0x72, 0x8e, 0x3e, 0x98, 0xf1, 0x2c, 0x2a, 0xc8, - 0xb3, 0x68, 0x8c, 0x5b, 0xdf, 0xff, 0x50, 0xa0, 0x4a, 0xf8, 0x72, 0x47, 0x6d, 0x9c, 0x4d, 0x0e, - 0xa9, 0xd2, 0xf5, 0x83, 0x92, 0xba, 0x7e, 0x68, 0xfc, 0x4d, 0x7c, 0xa2, 0xfc, 0x64, 0x52, 0x40, - 0xc1, 0x34, 0x3f, 0x9f, 0xa9, 0x79, 0xdf, 0x92, 0x9a, 0xbc, 0x65, 0x25, 0x7b, 0x85, 0x83, 0x49, - 0x40, 0xfe, 0x48, 0xdc, 0x95, 0x26, 0x00, 0x74, 0x11, 0x54, 0x7e, 0x12, 0x4f, 0x9c, 0x84, 0x2d, - 0x18, 0x33, 0xec, 0x10, 0x1e, 0xfb, 0xc6, 0x25, 0x50, 0x0d, 0x27, 0xc0, 0x86, 0xb5, 0xdf, 0x0a, - 0xf8, 0x0b, 0x16, 0x3a, 0xd2, 0x65, 0x7d, 0x96, 0xc3, 0xc5, 0xc3, 0x16, 0xed, 0x37, 0x64, 0xcf, - 0xa3, 0xb6, 0x74, 0xad, 0xc6, 0xf7, 0xa4, 0xdc, 0xd1, 0x80, 0x75, 0x3e, 0x4b, 0x91, 0x42, 0xa6, - 0x22, 0xb7, 0x61, 0x9a, 0x61, 0xb2, 0x4d, 0x48, 0xe4, 0x7a, 0xcf, 0xe7, 0x14, 0xa6, 0xf4, 0x6c, - 0x6e, 0x7a, 0xad, 0xcd, 0x8a, 0x2f, 0x28, 0x6d, 0xe3, 0x81, 0x30, 0x74, 0x1f, 0x57, 0xe5, 0xe8, - 0x5c, 0xb5, 0x1f, 0x2b, 0x30, 0x43, 0x0d, 0xb0, 0x63, 0x04, 0x6c, 0x89, 0x6b, 0xfc, 0xe5, 0x11, - 0xee, 0xf1, 0x12, 0xaf, 0x2d, 0x8c, 0xe7, 0xb5, 0xab, 0x50, 0xe6, 0x63, 0x14, 0xf0, 0x2c, 0xc0, - 0xe8, 0x0b, 0x69, 0x4c, 0x99, 0x64, 0x5f, 0x5c, 0x40, 0x0c, 0x33, 0x92, 0x16, 0xed, 0xc6, 0xfd, - 0xa4, 0x43, 0xb2, 0x3c, 0xe5, 0xf1, 0xe5, 0xbd, 0x0b, 0xd3, 0x5c, 0x1e, 0xeb, 0x57, 0xe3, 0x46, - 0x22, 0xea, 0xa8, 0x33, 0x39, 0xe1, 0xfe, 0x3a, 0xcc, 0xdd, 0xb4, 0x83, 0x30, 0xba, 0x63, 0x84, - 0xd1, 0x0a, 0x0b, 0x10, 0x68, 0x64, 0xb6, 0x4d, 0x80, 0xfc, 0x25, 0x3f, 0x6b, 0xd0, 0x7c, 0xb0, - 0x11, 0x46, 0xfc, 0xad, 0x2f, 0xfd, 0xad, 0xfd, 0x93, 0x02, 0xc7, 0x78, 0xde, 0x42, 0xaa, 0x7a, - 0x62, 0x27, 0x61, 0x6c, 0x38, 0xd8, 0x6a, 0x6d, 0x79, 0x8f, 0xc4, 0x44, 0x63, 0x90, 0x1b, 0xde, - 0x23, 0xb2, 0x33, 0x06, 0xc6, 0xc3, 0x56, 0xe0, 0xb1, 0xa2, 0x3f, 0x3e, 0xc9, 0xaa, 0x81, 0xf1, - 0x50, 0xe7, 0xa0, 0xc6, 0xfb, 0x0a, 0x14, 0x09, 0xaa, 0x14, 0x79, 0x2b, 0xe9, 0xc8, 0x7b, 0x1e, - 0x26, 0xe9, 0x5f, 0x7c, 0x10, 0xab, 0x11, 0x6d, 0x8c, 0xb1, 0x1a, 0xf5, 0x3e, 0x73, 0xa8, 0x65, - 0x56, 0x01, 0xfc, 0x5a, 0x81, 0xe3, 0x3a, 0xde, 0x0e, 0x70, 0xb8, 0x93, 0x2e, 0x01, 0x6e, 0xbc, - 0x32, 0xe4, 0xb8, 0x35, 0x0f, 0x93, 0xac, 0x90, 0xa1, 0xc0, 0x92, 0x45, 0xac, 0x8e, 0xe1, 0xad, - 0x23, 0xd6, 0xe2, 0x12, 0x43, 0x44, 0x76, 0x07, 0x7b, 0xdd, 0x48, 0xa4, 0x70, 0x78, 0xb3, 0xf1, - 0x8e, 0x98, 0xb6, 0x4d, 0xa8, 0xd2, 0xa3, 0x60, 0x6b, 0xdb, 0xeb, 0xba, 0x16, 0x9f, 0xb4, 0x57, - 0x33, 0x7d, 0x22, 0xb3, 0x4b, 0xec, 0x38, 0x09, 0x94, 0xc7, 0x4d, 0xc2, 0xe2, 0xb2, 0x0d, 0xc9, - 0x8d, 0x3e, 0x3a, 0xc1, 0x0b, 0x02, 0x59, 0x35, 0x84, 0x85, 0xb7, 0x6d, 0x17, 0x5b, 0xea, 0x53, - 0x68, 0x9e, 0xd7, 0x70, 0x11, 0x38, 0xdf, 0xc0, 0x55, 0x25, 0x05, 0xe5, 0x62, 0x58, 0x29, 0x44, - 0x0c, 0x95, 0xaa, 0x40, 0xd5, 0xe2, 0xe5, 0x2f, 0x55, 0xa0, 0x92, 0x5c, 0x5c, 0x9f, 0x00, 0x14, - 0x37, 0x64, 0x59, 0x67, 0x61, 0x21, 0x86, 0xdf, 0x4a, 0x56, 0x19, 0x36, 0xba, 0xf4, 0xb1, 0x95, - 0xaa, 0xf4, 0x23, 0xc9, 0x6f, 0xd0, 0x19, 0x52, 0x01, 0x2d, 0xc0, 0xd3, 0x31, 0x52, 0xff, 0x23, - 0x5f, 0x15, 0xa3, 0x67, 0xe1, 0x54, 0x26, 0xc2, 0x1d, 0xbc, 0x1d, 0xa9, 0xdb, 0xe8, 0x32, 0x9c, - 0xef, 0xfd, 0x9c, 0xfd, 0x94, 0x56, 0x6d, 0xa3, 0x4b, 0x70, 0x6e, 0x30, 0xae, 0x78, 0xea, 0xb0, - 0x83, 0xae, 0xc1, 0x0b, 0x83, 0x51, 0xd3, 0x2f, 0x61, 0x55, 0x1b, 0x2d, 0xc1, 0xe2, 0x60, 0x8a, - 0xfb, 0xdd, 0xa8, 0x4d, 0x4e, 0x51, 0xe2, 0xe9, 0xaa, 0xfa, 0x39, 0xb4, 0x08, 0x97, 0x47, 0xa3, - 0xd9, 0xc0, 0x6e, 0xa4, 0xee, 0x0e, 0x97, 0xb1, 0xee, 0x9a, 0x5e, 0xc7, 0x76, 0xdb, 0x62, 0xdb, - 0x53, 0x1d, 0xf4, 0x32, 0x5c, 0x1d, 0x8d, 0x26, 0x7e, 0x26, 0xa9, 0x76, 0x46, 0x17, 0x24, 0xde, - 0x37, 0xaa, 0x2e, 0xd2, 0xe0, 0x74, 0x0e, 0x0d, 0x7f, 0x69, 0xa8, 0x7a, 0xe8, 0x79, 0x38, 0x93, - 0x83, 0x13, 0xbf, 0x0d, 0x54, 0x7d, 0xa4, 0xc1, 0xb3, 0x31, 0x56, 0x4f, 0xbd, 0x3b, 0x73, 0x9b, - 0x1f, 0x29, 0xe8, 0x1a, 0x5c, 0x89, 0x71, 0x06, 0xd6, 0x6d, 0x33, 0x8a, 0x1f, 0x14, 0xd0, 0x2b, - 0x92, 0x21, 0xfa, 0x2b, 0x9f, 0xa5, 0x37, 0xf6, 0xcb, 0xae, 0xeb, 0x75, 0x5d, 0x13, 0x5b, 0xea, - 0x9f, 0x17, 0xd0, 0x22, 0x5c, 0xca, 0x97, 0x93, 0xaa, 0xdc, 0xc6, 0x96, 0xfa, 0x17, 0x05, 0x74, - 0x1e, 0x9e, 0xeb, 0xed, 0x61, 0xdf, 0x43, 0x44, 0xf5, 0x5b, 0x45, 0x74, 0x11, 0xce, 0x0e, 0xc2, - 0xe3, 0x2f, 0x04, 0xd5, 0x6f, 0x17, 0xd1, 0x69, 0x69, 0x02, 0xf4, 0xbe, 0xec, 0x53, 0xbf, 0x53, - 0x44, 0x67, 0x25, 0xbb, 0x67, 0x46, 0x9a, 0xea, 0x1f, 0x14, 0xd1, 0x05, 0xd0, 0x52, 0x48, 0x99, - 0x27, 0x1d, 0xf5, 0xbb, 0x69, 0xbd, 0xf2, 0x4f, 0x22, 0xea, 0x1f, 0x16, 0xd1, 0x4b, 0xfd, 0x53, - 0x64, 0xd0, 0x81, 0x41, 0xfd, 0x65, 0x31, 0x65, 0x9c, 0x54, 0xa1, 0x2d, 0x3f, 0xbf, 0x52, 0x37, - 0xff, 0x97, 0xd2, 0xe5, 0x6f, 0x88, 0x8a, 0x8a, 0x8c, 0x42, 0x20, 0x74, 0x0e, 0x9e, 0xcb, 0xfb, - 0xd6, 0xb3, 0x44, 0xe5, 0xa1, 0xf1, 0x3d, 0x52, 0x55, 0x88, 0x3f, 0xe6, 0x23, 0x31, 0xd5, 0xd4, - 0xc2, 0xe5, 0xbf, 0x55, 0xe2, 0xd7, 0x21, 0xec, 0x89, 0xd4, 0xa9, 0xf8, 0x21, 0x0a, 0x6d, 0xcb, - 0x62, 0x7b, 0x3e, 0x3d, 0xf0, 0xf8, 0x84, 0x51, 0x15, 0xb2, 0xe8, 0xca, 0x9f, 0xe2, 0x39, 0x5a, - 0x40, 0xc7, 0x61, 0x4e, 0xfe, 0xc2, 0x9c, 0xa4, 0x88, 0x4e, 0xc6, 0xcf, 0x3d, 0x38, 0x01, 0xf3, - 0x89, 0x89, 0x5e, 0x21, 0xc9, 0xcc, 0x9d, 0xec, 0xa5, 0x11, 0x53, 0x6f, 0xea, 0xf2, 0x2d, 0xa8, - 0xc4, 0xb9, 0x2f, 0x34, 0x03, 0xc0, 0x33, 0x4d, 0xab, 0x76, 0xa0, 0x3e, 0x45, 0xda, 0xeb, 0xee, - 0x16, 0xd9, 0x6b, 0x48, 0x5b, 0x41, 0xb3, 0x50, 0xbd, 0xdf, 0x8d, 0x62, 0x40, 0x01, 0x55, 0x60, - 0xf2, 0x86, 0x4d, 0x7e, 0x16, 0x97, 0x3e, 0xb8, 0x02, 0xb3, 0xe2, 0xaf, 0xe0, 0x88, 0x5a, 0x8d, - 0x30, 0xe3, 0x01, 0x27, 0x5a, 0x1c, 0x74, 0xa9, 0x98, 0xe0, 0x2d, 0xc6, 0xaf, 0x3c, 0x47, 0xc6, - 0xf7, 0x9d, 0xfd, 0x6b, 0x0a, 0xfa, 0xb2, 0x92, 0xfb, 0xce, 0x13, 0xbd, 0x32, 0xd6, 0x13, 0x3e, - 0xa1, 0xc1, 0xd2, 0x98, 0x54, 0x64, 0xb7, 0x27, 0x5a, 0xe4, 0x6c, 0x0d, 0x39, 0x5a, 0xe4, 0x60, - 0x0f, 0xd1, 0x22, 0x9f, 0x8a, 0x68, 0xf1, 0x85, 0x9c, 0xc7, 0x6f, 0x68, 0x14, 0x66, 0x1c, 0x37, - 0x56, 0xe0, 0xda, 0x58, 0x34, 0x44, 0xfc, 0xe7, 0xb3, 0x9f, 0xd3, 0xa1, 0x97, 0x46, 0xe0, 0xc4, - 0x50, 0x63, 0xe1, 0x57, 0xc7, 0x21, 0x21, 0xb2, 0xbf, 0xad, 0x0c, 0x7e, 0x69, 0x87, 0x5e, 0x1b, - 0xc9, 0x9e, 0x32, 0x49, 0xac, 0xcc, 0xab, 0x47, 0x21, 0x25, 0x4a, 0x45, 0x59, 0x4f, 0xf2, 0xd0, - 0x28, 0x7d, 0x23, 0x88, 0xb1, 0xfc, 0x17, 0x47, 0x27, 0xc8, 0x1c, 0x06, 0xb6, 0x3d, 0x8f, 0x34, - 0x0c, 0x0c, 0x75, 0xac, 0x61, 0x88, 0x49, 0xf2, 0x3c, 0x90, 0xac, 0x4a, 0xa3, 0x7a, 0x20, 0xc1, - 0x1d, 0xd7, 0x03, 0x39, 0x0d, 0x11, 0xbf, 0x95, 0x7e, 0xc0, 0x87, 0x2e, 0x0d, 0xe2, 0x40, 0x51, - 0x62, 0x61, 0x17, 0x46, 0x41, 0x25, 0x32, 0x76, 0x7a, 0x9f, 0xf4, 0xa1, 0x2b, 0x83, 0x48, 0x39, - 0x52, 0x2c, 0xe7, 0xd2, 0x68, 0xc8, 0x44, 0xd2, 0xc3, 0xcc, 0x87, 0x7e, 0x68, 0xa0, 0x59, 0x64, - 0xcc, 0x58, 0xe6, 0xe2, 0x18, 0x14, 0x44, 0xf0, 0x17, 0x95, 0xbc, 0xf7, 0x80, 0xe8, 0xe5, 0xec, - 0xb4, 0x43, 0x26, 0x72, 0x2c, 0xff, 0xa5, 0xf1, 0x88, 0xb8, 0x13, 0x67, 0xbd, 0x1d, 0x44, 0xa3, - 0xb1, 0x22, 0xa8, 0x43, 0x9c, 0x38, 0x87, 0x84, 0x3b, 0x71, 0xe6, 0xcb, 0xc2, 0x1c, 0x27, 0xce, - 0xc4, 0x1d, 0xe2, 0xc4, 0x79, 0x34, 0x44, 0xfc, 0x0f, 0x94, 0x11, 0x1f, 0x21, 0xa2, 0x1b, 0x23, - 0xf1, 0xce, 0xa4, 0x8d, 0xf5, 0xfb, 0xe4, 0x63, 0xf1, 0x20, 0xfa, 0xfe, 0xd1, 0xd0, 0xe7, 0x8c, - 0xe8, 0xfa, 0x68, 0x42, 0x52, 0x44, 0xb1, 0x86, 0xaf, 0x1d, 0x8d, 0x98, 0xa8, 0xf6, 0xc7, 0x23, - 0xbc, 0x6f, 0x44, 0xaf, 0x8f, 0xc4, 0xbf, 0x97, 0x2c, 0x56, 0xef, 0xfa, 0x51, 0xc9, 0x89, 0x82, - 0xbb, 0x7d, 0x8f, 0x1d, 0x51, 0x76, 0x00, 0xd4, 0x83, 0x15, 0x4b, 0xbf, 0x3c, 0x22, 0x36, 0x5f, - 0xb9, 0xd2, 0xcf, 0x19, 0x73, 0x56, 0xae, 0x34, 0xd2, 0x90, 0x95, 0xab, 0x0f, 0x99, 0x48, 0x72, - 0x33, 0x9e, 0xd0, 0xe5, 0x44, 0x82, 0x7d, 0x78, 0x43, 0x56, 0xe4, 0xfe, 0x67, 0x90, 0xd7, 0x14, - 0xb4, 0xdb, 0xff, 0x72, 0x0d, 0xbd, 0x38, 0x88, 0x3c, 0x46, 0x8b, 0xa5, 0x9d, 0x1f, 0x8a, 0x2e, - 0x84, 0xbd, 0x23, 0x3d, 0x17, 0x43, 0x03, 0xc8, 0x68, 0x21, 0x91, 0x60, 0xff, 0xfc, 0x50, 0x3c, - 0x62, 0x37, 0xdc, 0xf3, 0x12, 0x0b, 0xe5, 0x0c, 0xaf, 0x8c, 0x13, 0x8b, 0xb8, 0x38, 0x12, 0x2e, - 0xf7, 0xba, 0x9e, 0x47, 0x58, 0x39, 0x5e, 0xd7, 0x83, 0x35, 0xc4, 0xeb, 0xfa, 0xb1, 0x89, 0xb0, - 0x30, 0xe3, 0x85, 0xd6, 0x20, 0x5f, 0x48, 0xbd, 0x4b, 0x1a, 0x7c, 0x2a, 0xc8, 0xc2, 0x67, 0xa7, - 0x82, 0x4e, 0xdf, 0x1b, 0x9c, 0xdc, 0x1e, 0xa6, 0xb0, 0x86, 0xf6, 0xb0, 0x17, 0x9b, 0x89, 0xfb, - 0x92, 0x92, 0xf7, 0x4c, 0x26, 0x67, 0xc3, 0xcc, 0x46, 0x1e, 0xb2, 0x61, 0xe6, 0x12, 0x31, 0x25, - 0xde, 0x95, 0x9f, 0x86, 0xa0, 0x0b, 0xf9, 0x2c, 0xd2, 0x63, 0x79, 0x6e, 0x38, 0x22, 0x19, 0xc6, - 0xaf, 0x0e, 0x78, 0xdf, 0x80, 0xfe, 0x4f, 0x3e, 0x8f, 0x0c, 0xf4, 0x58, 0xf4, 0xcb, 0xe3, 0x92, - 0x11, 0x45, 0xde, 0x95, 0xab, 0x1d, 0xd1, 0xd0, 0x22, 0xc2, 0xc1, 0xdd, 0x4c, 0x21, 0xf2, 0x98, - 0x2b, 0xa3, 0x4a, 0x3d, 0x27, 0xe6, 0xca, 0xc0, 0x1c, 0x12, 0x73, 0x65, 0x53, 0x88, 0x13, 0x64, - 0x4e, 0xad, 0x7e, 0xce, 0x09, 0x32, 0x07, 0x7b, 0xc8, 0x09, 0x32, 0x9f, 0x4a, 0xc4, 0x1e, 0x23, - 0x15, 0xbb, 0xe7, 0xc4, 0x1e, 0x23, 0xd1, 0x0e, 0x89, 0x3d, 0x46, 0xe5, 0x41, 0xf4, 0xfd, 0xeb, - 0x71, 0x2a, 0xd6, 0xd1, 0xcd, 0xf1, 0xe5, 0x65, 0x5a, 0x76, 0xf5, 0xb1, 0xf9, 0x10, 0xdd, 0xdf, - 0x57, 0x72, 0xeb, 0xda, 0x73, 0x46, 0x3c, 0x07, 0x7b, 0xc8, 0x88, 0xe7, 0x53, 0xb1, 0x75, 0x23, - 0xcc, 0x28, 0x2c, 0x1f, 0x9c, 0xb6, 0x49, 0xf0, 0x46, 0x4b, 0xdb, 0xa4, 0xf0, 0x99, 0xd0, 0xef, - 0x0d, 0xaf, 0xdf, 0x46, 0x1f, 0xcf, 0xb9, 0x2d, 0x19, 0x48, 0x15, 0x6b, 0xf4, 0xb1, 0x23, 0x52, - 0x93, 0xa1, 0xf9, 0x54, 0x52, 0x18, 0x88, 0x86, 0x94, 0xd0, 0x09, 0x71, 0xc3, 0x0a, 0xf8, 0x28, - 0xdf, 0x56, 0xea, 0xca, 0x1d, 0xe5, 0xdf, 0x45, 0x72, 0x8c, 0x21, 0xd1, 0x49, 0x1a, 0x53, 0x28, - 0xce, 0xef, 0xb7, 0xf3, 0x14, 0xe7, 0x9f, 0x87, 0x29, 0x9e, 0xa0, 0xf1, 0xd0, 0x31, 0x7d, 0x6d, - 0x9c, 0x13, 0x3a, 0xa6, 0x91, 0x86, 0x84, 0x8e, 0x7d, 0xc8, 0x3c, 0x67, 0xd2, 0x7f, 0xa5, 0x9b, - 0x93, 0x33, 0xe9, 0x47, 0x1c, 0x92, 0x33, 0xc9, 0x24, 0xe0, 0x81, 0x57, 0xea, 0x62, 0x37, 0x27, - 0xf0, 0x4a, 0xe1, 0x0c, 0x09, 0xbc, 0x7a, 0x71, 0xf9, 0xc9, 0x32, 0xf3, 0xb2, 0x2f, 0xe7, 0x64, - 0x99, 0x7d, 0x31, 0x38, 0xf8, 0x64, 0x99, 0x47, 0xe3, 0x3b, 0xfb, 0x37, 0x3e, 0xf2, 0xd3, 0x7f, - 0x3e, 0xfd, 0xd4, 0xdf, 0x1d, 0x9e, 0x56, 0x7e, 0x7a, 0x78, 0x5a, 0xf9, 0xf9, 0xe1, 0x69, 0xe5, - 0x33, 0xcf, 0x6f, 0xe1, 0x20, 0xda, 0x5f, 0x8c, 0xb0, 0xb9, 0x73, 0x95, 0x73, 0xbb, 0xea, 0xef, - 0xb6, 0xaf, 0xa6, 0xfe, 0x07, 0x8a, 0xad, 0x29, 0xda, 0x7c, 0xf9, 0xbf, 0x03, 0x00, 0x00, 0xff, - 0xff, 0xa9, 0x08, 0x9c, 0x77, 0x99, 0x62, 0x00, 0x00, + // 6383 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe4, 0x7c, 0x4b, 0x6c, 0x1c, 0xc9, + 0x79, 0xf0, 0xf6, 0x0c, 0xc9, 0x99, 0xf9, 0x86, 0x8f, 0x66, 0x89, 0x92, 0x46, 0xb3, 0x92, 0xa8, + 0x6d, 0xad, 0x9e, 0xbb, 0x4b, 0x69, 0xb9, 0xfb, 0x7b, 0xbd, 0x96, 0x77, 0x6d, 0x8a, 0xa4, 0xf4, + 0x73, 0xf5, 0x9a, 0x6d, 0x8a, 0x5e, 0xaf, 0xb1, 0xc8, 0xb8, 0xd9, 0x5d, 0x1c, 0xb6, 0xd9, 0xd3, + 0xdd, 0xdb, 0x0f, 0x52, 0x34, 0xe2, 0xc0, 0xb0, 0xbd, 0xb6, 0x83, 0x38, 0xf1, 0x2b, 0x0e, 0x02, + 0xc4, 0x08, 0x72, 0xc8, 0x2d, 0x70, 0x02, 0x18, 0x81, 0x81, 0x00, 0x39, 0xe5, 0x14, 0x07, 0x89, + 0xe1, 0x93, 0x2f, 0x01, 0x18, 0x87, 0x27, 0x07, 0x08, 0x82, 0x00, 0xb1, 0x93, 0x83, 0x81, 0x20, + 0xa8, 0x57, 0x77, 0xf5, 0xb0, 0x7b, 0x1e, 0x92, 0x16, 0x39, 0xe4, 0xc4, 0xa9, 0xaf, 0xbf, 0x57, + 0x7d, 0xf5, 0x55, 0xd5, 0x57, 0x5f, 0x7d, 0x45, 0x38, 0xe6, 0x07, 0x5e, 0xe4, 0x99, 0x9e, 0x13, + 0xed, 0xfb, 0x38, 0x5c, 0xa0, 0x2d, 0x74, 0x6c, 0x0f, 0x87, 0xdb, 0x2e, 0x8e, 0x16, 0xc4, 0xc7, + 0x85, 0xdd, 0x97, 0x9b, 0x73, 0x1d, 0xaf, 0xe3, 0x51, 0xc0, 0x35, 0xf2, 0x8b, 0x7d, 0x6b, 0x9e, + 0xf2, 0xe3, 0x70, 0x9b, 0xd2, 0x5e, 0x4b, 0x7e, 0xb1, 0x4f, 0xda, 0x3f, 0x28, 0x50, 0x59, 0x32, + 0x4d, 0x2f, 0x76, 0x23, 0x74, 0x1d, 0xc6, 0x3b, 0x81, 0x17, 0xfb, 0x0d, 0xe5, 0x9c, 0x72, 0xb9, + 0xbe, 0xd8, 0x5c, 0xc8, 0x91, 0xb0, 0x70, 0x9b, 0x60, 0xe8, 0x0c, 0x11, 0x2d, 0xc0, 0x31, 0x83, + 0x11, 0xb7, 0xfd, 0xc0, 0xde, 0x35, 0x22, 0xdc, 0xde, 0xc1, 0xfb, 0x8d, 0xd2, 0x39, 0xe5, 0xf2, + 0xa4, 0x3e, 0xcb, 0x3f, 0xb5, 0xd8, 0x97, 0x3b, 0x78, 0x1f, 0x5d, 0x85, 0x59, 0xc3, 0xb1, 0x8d, + 0x30, 0x83, 0x5d, 0xa6, 0xd8, 0x33, 0xf4, 0x83, 0x84, 0xfb, 0x2a, 0x9c, 0xf0, 0xe3, 0x4d, 0xc7, + 0x36, 0xdb, 0x01, 0x76, 0x2d, 0xfc, 0xf9, 0x5d, 0x2f, 0x0e, 0xdb, 0x21, 0xc6, 0x56, 0x63, 0x8c, + 0x12, 0xcc, 0xb1, 0xaf, 0x7a, 0xf2, 0x71, 0x1d, 0x63, 0x4b, 0xfb, 0xa5, 0x02, 0xe3, 0x54, 0x45, + 0x74, 0x06, 0x80, 0xd3, 0x13, 0x21, 0x0a, 0xa5, 0xa9, 0x31, 0x08, 0x61, 0x7f, 0x02, 0x26, 0x42, + 0x6c, 0x06, 0x38, 0xe2, 0xda, 0xf2, 0x16, 0x21, 0x63, 0xbf, 0xda, 0xa1, 0xdd, 0xe1, 0xba, 0xd5, + 0x18, 0x64, 0xdd, 0xee, 0xa0, 0x37, 0x00, 0x68, 0xd7, 0xdb, 0xc4, 0x88, 0x54, 0x93, 0xe9, 0xc5, + 0xb3, 0xc5, 0x86, 0x7a, 0xb8, 0xef, 0x63, 0xbd, 0xd6, 0x11, 0x3f, 0xd1, 0x29, 0xa8, 0x86, 0x76, + 0xc7, 0x6d, 0xfb, 0xf1, 0x66, 0x63, 0x9c, 0xf2, 0xae, 0x90, 0x76, 0x2b, 0xde, 0x24, 0x9f, 0x1c, + 0xdb, 0xdd, 0xa1, 0xda, 0x4e, 0xb0, 0x4f, 0xa4, 0x4d, 0x74, 0x3d, 0x07, 0x93, 0xe2, 0x13, 0xd5, + 0xaa, 0x42, 0x3f, 0x03, 0xff, 0xbc, 0x6e, 0x77, 0xb4, 0x5f, 0x29, 0xa0, 0x52, 0x81, 0xff, 0x1f, + 0x1b, 0x56, 0xb8, 0xfa, 0xc8, 0xf7, 0x82, 0x68, 0x90, 0x05, 0x64, 0x5d, 0x4a, 0x59, 0x5d, 0x56, + 0xe1, 0x58, 0x17, 0x47, 0x86, 0x65, 0x44, 0x46, 0x7b, 0x9b, 0x70, 0x6c, 0x9b, 0xb6, 0x15, 0x36, + 0xca, 0xe7, 0xca, 0x97, 0x27, 0x6f, 0x1e, 0x3f, 0x3c, 0x98, 0x9f, 0xbd, 0xc7, 0x3f, 0x53, 0x79, + 0xcb, 0x6b, 0x2b, 0xa1, 0x3e, 0xdb, 0xcd, 0x80, 0x6c, 0x2b, 0x64, 0x6c, 0xc2, 0xd0, 0xe8, 0xe0, + 0x50, 0x66, 0x33, 0x26, 0xb3, 0x61, 0x9f, 0x33, 0x6c, 0x64, 0x10, 0x61, 0x23, 0x5b, 0x66, 0x3c, + 0x63, 0x19, 0xed, 0x27, 0x0a, 0x4c, 0xd1, 0x7e, 0x0b, 0x7d, 0xc8, 0x00, 0xe1, 0x5d, 0xec, 0x46, + 0x6c, 0x80, 0x94, 0x3e, 0x03, 0xb4, 0x4a, 0xd0, 0xd8, 0x00, 0x61, 0xf1, 0x13, 0x35, 0xa0, 0xe2, + 0x1b, 0xfb, 0x8e, 0x67, 0x58, 0xc2, 0x26, 0xbc, 0x89, 0x54, 0x28, 0xa7, 0x1e, 0x41, 0x7e, 0x22, + 0x1d, 0x66, 0x05, 0xbf, 0xb6, 0xe8, 0x3c, 0x75, 0x89, 0xfa, 0xe2, 0x85, 0x5c, 0x89, 0x2d, 0xfe, + 0x5b, 0x28, 0xab, 0xab, 0x7e, 0x0f, 0x44, 0x5b, 0xe2, 0xfd, 0x59, 0x75, 0x77, 0xb1, 0xe3, 0xf9, + 0x18, 0xcd, 0xc1, 0xb8, 0xeb, 0xb9, 0x26, 0xe6, 0xe3, 0xc7, 0x1a, 0x04, 0x4a, 0x75, 0xe6, 0x4a, + 0xb2, 0xc6, 0x5b, 0x63, 0xd5, 0xb2, 0x3a, 0xa6, 0xfd, 0x87, 0x02, 0xd3, 0xdc, 0xae, 0xc4, 0x86, + 0x38, 0x08, 0x49, 0xaf, 0xe8, 0x54, 0xc4, 0x01, 0x65, 0x33, 0xa6, 0x8b, 0x26, 0xba, 0x02, 0x35, + 0x0b, 0xef, 0xda, 0x26, 0x6e, 0xfb, 0x3b, 0x8c, 0xd9, 0xcd, 0xc9, 0xc3, 0x83, 0xf9, 0xea, 0x0a, + 0x05, 0xb6, 0xee, 0xe8, 0x55, 0xf6, 0xb9, 0xb5, 0x93, 0x63, 0x80, 0x7b, 0x50, 0x95, 0xfa, 0x5d, + 0xbe, 0x5c, 0x5f, 0x7c, 0x39, 0xb7, 0xdf, 0x59, 0x6d, 0x16, 0x44, 0x67, 0x57, 0xdd, 0x28, 0xd8, + 0xd7, 0x13, 0x16, 0xcd, 0x1b, 0x30, 0x95, 0xf9, 0x44, 0x24, 0x0a, 0xcf, 0xad, 0xe9, 0xe4, 0x27, + 0xe9, 0xf7, 0xae, 0xe1, 0xc4, 0x98, 0xaa, 0x5a, 0xd3, 0x59, 0xe3, 0x63, 0xa5, 0x8f, 0x2a, 0x5a, + 0x03, 0xd4, 0x5e, 0xf3, 0xbe, 0x35, 0x56, 0x55, 0xd4, 0x92, 0xf6, 0x15, 0x05, 0xd4, 0x55, 0xd7, + 0x0c, 0xf6, 0xfd, 0x08, 0x5b, 0x5c, 0x15, 0x74, 0x1a, 0x6a, 0xbe, 0x63, 0xd8, 0x6e, 0x84, 0x1f, + 0x45, 0xc9, 0xd4, 0x10, 0x80, 0xfc, 0x91, 0x2d, 0x3d, 0xd9, 0xc8, 0xfa, 0x30, 0xc3, 0x85, 0x27, + 0x63, 0x7b, 0x09, 0x66, 0xb8, 0xb7, 0xd3, 0xe9, 0x81, 0x83, 0x90, 0xab, 0x32, 0xdd, 0x3d, 0x32, + 0x7e, 0x1c, 0x22, 0xbc, 0x92, 0x37, 0x53, 0xf7, 0x28, 0x4b, 0xee, 0xf1, 0xd6, 0x58, 0x75, 0x4c, + 0x1d, 0xd7, 0xbe, 0xa4, 0xc0, 0x24, 0x75, 0xf2, 0x65, 0x8f, 0x75, 0xeb, 0x04, 0x94, 0x6c, 0x8b, + 0x89, 0xb8, 0x39, 0x71, 0x78, 0x30, 0x5f, 0x5a, 0x5b, 0xd1, 0x4b, 0xb6, 0x85, 0x5e, 0x04, 0xf0, + 0x8d, 0x80, 0x4c, 0x1a, 0x32, 0x3d, 0x4b, 0x74, 0x7a, 0x4e, 0x1d, 0x1e, 0xcc, 0xd7, 0x5a, 0x14, + 0x4a, 0xa6, 0x65, 0x8d, 0x21, 0xac, 0x59, 0x21, 0xba, 0x08, 0x55, 0xb6, 0x04, 0xfa, 0x3b, 0x4c, + 0xea, 0xcd, 0xfa, 0xe1, 0xc1, 0x7c, 0x85, 0xba, 0x6d, 0xeb, 0x8e, 0x5e, 0xa1, 0x1f, 0x5b, 0x3b, + 0x5c, 0x09, 0x1d, 0xea, 0x4b, 0x7e, 0x3a, 0x3d, 0x33, 0xfe, 0xa6, 0xf4, 0xf5, 0xb7, 0xc2, 0x4e, + 0x6b, 0x1d, 0x40, 0xa4, 0x4b, 0x86, 0x19, 0x2d, 0x59, 0xd6, 0x12, 0xd9, 0x37, 0xc8, 0x7a, 0x36, + 0x02, 0xeb, 0x8b, 0x50, 0xe5, 0xfb, 0x90, 0x70, 0x7a, 0xda, 0x05, 0xca, 0x8a, 0x74, 0x81, 0xed, + 0x45, 0x3b, 0xda, 0xef, 0x28, 0x30, 0x47, 0xfb, 0xb5, 0x64, 0x59, 0xf7, 0x70, 0x77, 0x13, 0x07, + 0x8c, 0x19, 0x91, 0xd5, 0xa5, 0xed, 0x1e, 0x59, 0x0c, 0x89, 0xc8, 0x62, 0x9f, 0x5b, 0x3b, 0xa3, + 0xcc, 0xb0, 0x33, 0x00, 0x9c, 0xab, 0xb4, 0xf7, 0x30, 0x08, 0x59, 0xe4, 0x6f, 0xc3, 0x34, 0x23, + 0x5a, 0xde, 0x36, 0x6c, 0x97, 0x74, 0xf9, 0x59, 0xa8, 0x99, 0xe4, 0xb7, 0xb4, 0xc0, 0x57, 0x4d, + 0xf1, 0x51, 0x9a, 0xf4, 0xa5, 0xcc, 0xa4, 0xd7, 0xbe, 0xa7, 0xc0, 0x09, 0xd1, 0xad, 0x1e, 0x8e, + 0x23, 0x18, 0xf1, 0x23, 0x30, 0x6d, 0xe1, 0x30, 0x6a, 0xa7, 0x86, 0x60, 0xbd, 0x53, 0x0f, 0x0f, + 0xe6, 0x27, 0x57, 0x70, 0x18, 0x25, 0xc6, 0x98, 0xb4, 0xd2, 0xd6, 0x8e, 0xbc, 0xc4, 0x96, 0x33, + 0x4b, 0x2c, 0xd1, 0xeb, 0xdc, 0xbd, 0xd8, 0x89, 0x6c, 0x86, 0x2b, 0x54, 0xa4, 0xc3, 0xa2, 0xe3, + 0xd0, 0x73, 0x76, 0x7b, 0x57, 0xac, 0xfe, 0x1a, 0x5e, 0x80, 0x69, 0x36, 0xcc, 0x01, 0x27, 0xe6, + 0x8e, 0x34, 0x65, 0x64, 0x38, 0xce, 0x43, 0x5d, 0x44, 0x25, 0x9e, 0xb7, 0xc5, 0x95, 0x02, 0x1e, + 0x8f, 0x78, 0xde, 0x96, 0xf6, 0x35, 0x05, 0x4e, 0x65, 0xf4, 0x32, 0xdc, 0x68, 0xc9, 0xea, 0xda, + 0xae, 0xee, 0x39, 0x78, 0x14, 0x85, 0x3e, 0x01, 0xb3, 0x1d, 0x42, 0x8c, 0xf1, 0x11, 0xab, 0x1d, + 0x3b, 0x3c, 0x98, 0x9f, 0xb9, 0xcd, 0x3e, 0x26, 0x86, 0x9b, 0xe9, 0x64, 0x00, 0x3b, 0xda, 0x2a, + 0x34, 0x24, 0x45, 0xd6, 0x5c, 0x3b, 0xb2, 0x0d, 0x87, 0x35, 0x46, 0xf0, 0x49, 0xcd, 0x80, 0x73, + 0x89, 0x71, 0x2d, 0xcb, 0x8e, 0x6c, 0xcf, 0x35, 0x9c, 0x6c, 0x24, 0x35, 0x4a, 0xb7, 0x10, 0x8c, + 0xd1, 0xc0, 0x8c, 0x59, 0x97, 0xfe, 0xd6, 0x2c, 0x38, 0xcf, 0x42, 0x45, 0xdc, 0xf5, 0x76, 0xf1, + 0x87, 0x25, 0xe5, 0x7d, 0x40, 0x3c, 0x7a, 0xa5, 0xc2, 0xde, 0xf2, 0x6c, 0x77, 0x34, 0xa6, 0x49, + 0xcc, 0x5b, 0x1a, 0x32, 0xe6, 0xd5, 0x30, 0xa8, 0xb2, 0xc8, 0xbb, 0x78, 0x2b, 0x1a, 0x71, 0xe9, + 0x49, 0x56, 0xcf, 0x52, 0xf1, 0xea, 0xa9, 0xbd, 0x05, 0x67, 0xb8, 0x18, 0xbe, 0xd4, 0xe9, 0xf8, + 0xfd, 0x18, 0x87, 0xd1, 0x8a, 0x1d, 0x1a, 0x9b, 0xce, 0x48, 0x9d, 0xd4, 0xd6, 0xe0, 0x74, 0x2e, + 0xaf, 0x55, 0x77, 0x64, 0x56, 0x5f, 0x55, 0xe0, 0x7c, 0x2e, 0x2f, 0x1d, 0x6f, 0xe1, 0x00, 0xbb, + 0x26, 0xd6, 0x71, 0x88, 0x47, 0xb2, 0x48, 0x71, 0xa0, 0x5f, 0xea, 0x13, 0xe8, 0xff, 0x4c, 0x29, + 0x30, 0xd0, 0xaa, 0xfb, 0x7e, 0x8c, 0xe3, 0xd1, 0xbc, 0x60, 0xc8, 0x41, 0x41, 0x9f, 0x20, 0x4b, + 0x2a, 0x15, 0x46, 0x57, 0x89, 0xa2, 0x68, 0x60, 0x7d, 0xdb, 0x08, 0x30, 0x31, 0xad, 0xd0, 0x4c, + 0x50, 0xa1, 0xe7, 0x60, 0xd2, 0xdb, 0x73, 0xb3, 0xd1, 0xe2, 0xa4, 0x5e, 0xf7, 0xf6, 0xdc, 0x24, + 0x4e, 0x88, 0xe0, 0x54, 0x6e, 0xbf, 0xd6, 0xb1, 0x3b, 0x92, 0x59, 0x5f, 0x04, 0xe0, 0x52, 0xd3, + 0x5e, 0xd1, 0x4d, 0x9d, 0xb3, 0x6d, 0xdd, 0xd1, 0x6b, 0x1c, 0xa1, 0xb5, 0xa3, 0xfd, 0x63, 0x91, + 0x39, 0x75, 0x6c, 0x62, 0x7b, 0x77, 0x34, 0x73, 0x8e, 0x24, 0x1a, 0x7d, 0x04, 0x4e, 0x0a, 0xec, + 0x5e, 0x07, 0x60, 0x4b, 0xf1, 0x71, 0x53, 0x68, 0xd4, 0xb3, 0x74, 0xa8, 0x82, 0xae, 0xc7, 0x9e, + 0x33, 0x1c, 0x9e, 0xd8, 0x74, 0x1f, 0xce, 0x16, 0x4d, 0x26, 0xd3, 0x08, 0xac, 0x0f, 0xb1, 0x77, + 0xda, 0x9f, 0x14, 0x19, 0x76, 0xc9, 0x34, 0x31, 0x89, 0x48, 0x3f, 0x3c, 0xc3, 0x0e, 0x19, 0xa8, + 0x69, 0x3e, 0x1c, 0xcf, 0x6a, 0x78, 0xd3, 0xf1, 0xcc, 0x9d, 0x0f, 0xd3, 0x28, 0x01, 0x9c, 0xcc, + 0x4a, 0xdc, 0x70, 0x37, 0x3f, 0x6c, 0x99, 0xbf, 0xab, 0x40, 0x83, 0x0b, 0x5d, 0xc7, 0x01, 0x61, + 0xf1, 0xd0, 0xdb, 0xc1, 0xee, 0x92, 0x35, 0xe2, 0xf0, 0xdf, 0x82, 0xa9, 0x90, 0xd1, 0xb7, 0x23, + 0xc2, 0x80, 0xef, 0x1c, 0xcf, 0xe5, 0xaf, 0x04, 0x92, 0x24, 0x7d, 0x32, 0x94, 0x5a, 0x9a, 0x07, + 0xcd, 0x1c, 0x75, 0xd8, 0x76, 0x39, 0xea, 0xe2, 0x45, 0x15, 0x69, 0xdb, 0x6c, 0xc5, 0xac, 0xb1, + 0x61, 0xa6, 0xec, 0xd6, 0x56, 0xf4, 0x0a, 0xfd, 0xb8, 0x66, 0x69, 0x3f, 0x14, 0x39, 0x02, 0x1d, + 0xfb, 0x8e, 0x6d, 0x1a, 0x91, 0xed, 0x76, 0x46, 0x91, 0xb3, 0x02, 0xc8, 0x88, 0xa3, 0x6d, 0xec, + 0x46, 0x94, 0xd8, 0x73, 0xdb, 0x71, 0xe0, 0x70, 0x89, 0xf4, 0x30, 0xbf, 0x94, 0xf9, 0xba, 0xa1, + 0xdf, 0xd5, 0x67, 0xb3, 0x04, 0x1b, 0x81, 0x83, 0x5e, 0x02, 0x14, 0x08, 0xf9, 0x9e, 0xdb, 0x26, + 0x26, 0xc1, 0x01, 0x75, 0xcf, 0x9a, 0x3e, 0x2b, 0x7d, 0x59, 0xa7, 0x1f, 0xb4, 0xbb, 0x30, 0xcb, + 0xcd, 0xc3, 0x92, 0x1a, 0x2b, 0xe4, 0xa0, 0x58, 0x83, 0x0a, 0x9f, 0x44, 0xcd, 0x17, 0x61, 0x9c, + 0x74, 0x67, 0x1f, 0x9d, 0x87, 0x29, 0x4c, 0x31, 0xb0, 0xd5, 0xa6, 0x4b, 0x01, 0x0b, 0x87, 0x27, + 0x05, 0x90, 0x10, 0x6a, 0xbf, 0x9a, 0x80, 0x93, 0x9c, 0xdd, 0x6d, 0x4c, 0x7c, 0x6f, 0xcb, 0xee, + 0xc4, 0x01, 0x95, 0x27, 0x33, 0xfd, 0x60, 0x42, 0x70, 0x7d, 0x11, 0x20, 0x49, 0x70, 0x09, 0xfb, + 0x50, 0x17, 0xe3, 0x43, 0x47, 0x5c, 0x4c, 0xa4, 0xb9, 0x46, 0x0a, 0xf5, 0x3f, 0x0e, 0xaa, 0x60, + 0xdc, 0x33, 0x47, 0xd1, 0xe1, 0xc1, 0xfc, 0xb4, 0x1c, 0x61, 0xb4, 0xee, 0xe8, 0xd3, 0x86, 0xdc, + 0xde, 0x41, 0xe7, 0xa1, 0xe2, 0x63, 0x1c, 0x90, 0x11, 0x1f, 0xa3, 0xf6, 0x87, 0xc3, 0x83, 0xf9, + 0x89, 0x16, 0xc6, 0xc1, 0xda, 0x8a, 0x3e, 0x41, 0x3e, 0xad, 0x59, 0xe4, 0x88, 0xeb, 0xd8, 0x61, + 0x84, 0x5d, 0x72, 0xae, 0x1c, 0x3f, 0x57, 0xbe, 0x5c, 0xd3, 0x53, 0x00, 0xfa, 0x0c, 0xd4, 0x37, + 0x1d, 0xdc, 0xc6, 0x2c, 0x04, 0xa0, 0x19, 0xa7, 0xe9, 0xc5, 0xd7, 0xfb, 0x39, 0x71, 0xaf, 0xc5, + 0x16, 0xd6, 0x71, 0x44, 0x7c, 0x68, 0x3d, 0x32, 0x22, 0xac, 0xc3, 0xa6, 0x83, 0x45, 0x3c, 0x61, + 0x82, 0xba, 0x67, 0x6f, 0xd9, 0x6d, 0x7f, 0xd1, 0x4f, 0x04, 0x54, 0x9e, 0x54, 0xc0, 0x34, 0x61, + 0xd9, 0x5a, 0xf4, 0x85, 0x90, 0xf7, 0x60, 0xb2, 0x6b, 0xb9, 0x61, 0x22, 0xa0, 0xfa, 0xa4, 0x02, + 0xea, 0x84, 0x9d, 0xe0, 0xfe, 0x1b, 0x30, 0x15, 0x60, 0xc7, 0xd8, 0x4f, 0xd8, 0xd7, 0x9e, 0x94, + 0xfd, 0x24, 0xe5, 0x27, 0xf8, 0x3f, 0x84, 0x59, 0xe1, 0x2a, 0x71, 0xb8, 0xcd, 0x57, 0x12, 0xa0, + 0x2b, 0xc9, 0xe5, 0xfc, 0x0c, 0x43, 0x1c, 0x6e, 0x73, 0x39, 0x7c, 0x4b, 0x0e, 0xf4, 0x19, 0xee, + 0x4e, 0x71, 0xb8, 0x4d, 0x67, 0x3b, 0xba, 0x07, 0x48, 0xe6, 0xca, 0x27, 0x57, 0x9d, 0xb2, 0x9d, + 0xef, 0xcb, 0x16, 0x07, 0xba, 0x9a, 0x72, 0xe3, 0x93, 0xef, 0x36, 0x4c, 0xca, 0x5d, 0x40, 0x75, + 0xa8, 0x6c, 0xb8, 0x3b, 0xae, 0xb7, 0xe7, 0xaa, 0xcf, 0x90, 0x06, 0xef, 0x8c, 0xaa, 0xa0, 0x49, + 0xa8, 0x8a, 0xc0, 0x54, 0x2d, 0xa1, 0x19, 0xa8, 0x6f, 0xb8, 0xc6, 0xae, 0x61, 0x3b, 0x04, 0xa2, + 0x96, 0xb5, 0x2f, 0xc0, 0xc9, 0x82, 0x68, 0x51, 0x9e, 0x76, 0xef, 0x88, 0x59, 0x57, 0x1c, 0x11, + 0x2a, 0xc5, 0x11, 0x21, 0x39, 0x57, 0x8a, 0xc1, 0x22, 0x73, 0xaf, 0xaa, 0x8b, 0xa6, 0xf6, 0x02, + 0x1c, 0xcf, 0x0d, 0xa2, 0x65, 0xe1, 0x15, 0x2e, 0x5c, 0xfb, 0x2c, 0xcc, 0xe5, 0x45, 0xc9, 0x32, + 0xee, 0x1b, 0x4f, 0xa4, 0xa8, 0xb6, 0x0d, 0xa7, 0x7b, 0xad, 0x11, 0xe2, 0x7c, 0x93, 0x3c, 0xa1, + 0xa4, 0x6f, 0x28, 0x49, 0xa6, 0x24, 0x8d, 0x22, 0xad, 0x66, 0x37, 0x11, 0x20, 0x47, 0xb4, 0xca, + 0x53, 0x89, 0x68, 0x4b, 0x47, 0x22, 0xda, 0xd4, 0xb4, 0x9f, 0xee, 0x35, 0x2d, 0x8b, 0x81, 0x9a, + 0xaf, 0xa5, 0xfa, 0x64, 0xf7, 0x74, 0xa5, 0xff, 0x9e, 0x9e, 0x72, 0x7e, 0x37, 0x67, 0x84, 0x49, + 0x64, 0xf7, 0x14, 0x58, 0xb7, 0x60, 0x52, 0x0e, 0x8b, 0x9e, 0x02, 0x47, 0x1d, 0xa6, 0xb3, 0x61, + 0xcf, 0x53, 0xe0, 0xf9, 0x36, 0x1c, 0x13, 0x29, 0x31, 0x9e, 0x0f, 0xa3, 0x23, 0xfd, 0x72, 0xca, + 0x58, 0x8e, 0x06, 0x95, 0xe2, 0x68, 0x30, 0x65, 0xf9, 0x10, 0x4e, 0xf4, 0x26, 0x63, 0x96, 0x03, + 0x6c, 0x44, 0x19, 0x07, 0xbd, 0x26, 0x1c, 0x74, 0x48, 0xf6, 0xda, 0x7b, 0x30, 0xd7, 0xcb, 0x95, + 0x9c, 0xda, 0x9b, 0x37, 0x52, 0x4d, 0x47, 0xbe, 0x87, 0x4a, 0x75, 0x5e, 0x87, 0xe3, 0xbd, 0xdc, + 0xef, 0x62, 0x63, 0x17, 0x3f, 0x91, 0x21, 0x4c, 0xb8, 0x70, 0x24, 0x2b, 0x25, 0x27, 0x90, 0x88, + 0xaf, 0x39, 0x5e, 0xf8, 0x64, 0x42, 0xbe, 0xa6, 0xc0, 0xd9, 0xa3, 0xb9, 0x2f, 0x9e, 0x63, 0xa2, + 0x79, 0xa1, 0xe6, 0x7b, 0x23, 0xb3, 0xcf, 0xe6, 0x84, 0x4a, 0xfd, 0x72, 0x42, 0xa9, 0x26, 0xdf, + 0xca, 0xc9, 0xc2, 0xad, 0xb9, 0xbb, 0x76, 0x44, 0x37, 0x35, 0xee, 0x02, 0x8f, 0xd1, 0xd5, 0xd7, + 0x85, 0xab, 0x8c, 0x3c, 0xbe, 0xda, 0xd7, 0x15, 0x98, 0x91, 0xb2, 0xc8, 0xd4, 0xb5, 0xdf, 0x1e, + 0xdd, 0x1a, 0x85, 0x97, 0x3b, 0xec, 0xe6, 0xa4, 0xa9, 0x09, 0x0d, 0x4f, 0x41, 0xd9, 0x4c, 0x32, + 0xe5, 0x95, 0xc3, 0x83, 0xf9, 0xf2, 0xf2, 0xda, 0x8a, 0x4e, 0x60, 0x64, 0x9c, 0xa6, 0xa9, 0x2a, + 0x34, 0x15, 0xfd, 0xbf, 0xa9, 0xc9, 0x0f, 0x15, 0x40, 0x99, 0xbb, 0x2f, 0x9a, 0xeb, 0x27, 0xe7, + 0x13, 0x76, 0x01, 0x66, 0x7a, 0xe9, 0xed, 0x46, 0xd1, 0xf9, 0x44, 0xbe, 0x1e, 0xd0, 0x27, 0xb1, + 0x7c, 0x59, 0xf0, 0xa6, 0x74, 0xb9, 0xc3, 0x8e, 0x38, 0x5a, 0xf1, 0x40, 0x25, 0xf7, 0x1e, 0x09, + 0x4d, 0x7a, 0x45, 0x55, 0x96, 0xae, 0xa8, 0xb4, 0xbf, 0x54, 0x60, 0x96, 0x53, 0xb0, 0xbb, 0x90, + 0xa7, 0xaa, 0xf3, 0x1b, 0x50, 0x11, 0x17, 0x29, 0x4c, 0xe5, 0xf3, 0x43, 0xdc, 0x47, 0xe9, 0x82, + 0x46, 0xbe, 0x71, 0x28, 0x67, 0x6f, 0x1c, 0xfe, 0x33, 0x55, 0x9b, 0x75, 0xef, 0xae, 0x1d, 0x46, + 0xcd, 0x9f, 0x2b, 0xa3, 0x8f, 0xfc, 0x45, 0xa8, 0x86, 0xb6, 0x6b, 0x62, 0x71, 0x5a, 0xe3, 0x78, + 0xeb, 0x04, 0x46, 0x4e, 0x6b, 0xf4, 0xe3, 0x9a, 0x85, 0x9e, 0x85, 0x1a, 0xc3, 0x73, 0xbd, 0x3d, + 0xaa, 0x4d, 0x55, 0x67, 0x84, 0xf7, 0xbd, 0x3d, 0xc2, 0x24, 0x76, 0x23, 0xdb, 0x11, 0x07, 0x00, + 0xce, 0x64, 0x83, 0xc0, 0x08, 0x13, 0xfa, 0x91, 0x31, 0x61, 0x78, 0x84, 0xc9, 0x38, 0x63, 0x42, + 0x01, 0x84, 0xc9, 0x79, 0x12, 0xe2, 0xee, 0xe2, 0x20, 0xc4, 0x6d, 0x2f, 0xb0, 0x70, 0x40, 0xcf, + 0x00, 0x55, 0x12, 0xa7, 0x52, 0xe0, 0x03, 0x02, 0x4b, 0x2f, 0x96, 0xb9, 0xcd, 0xfe, 0xaf, 0xf4, + 0xfb, 0xbf, 0x15, 0xa8, 0xf1, 0x95, 0x6f, 0xcb, 0x6b, 0xb6, 0x47, 0xef, 0xef, 0x48, 0xc9, 0x89, + 0xe6, 0x37, 0x95, 0xc7, 0x5e, 0x1c, 0x47, 0x58, 0xe3, 0xb3, 0x07, 0xd4, 0x72, 0xdf, 0x44, 0xef, + 0xe7, 0x60, 0x6a, 0xc9, 0x8c, 0x68, 0x35, 0x06, 0x95, 0xd6, 0x6c, 0x8d, 0x6e, 0x83, 0x33, 0x00, + 0x8e, 0x67, 0x1a, 0x4e, 0xdb, 0x73, 0x9d, 0x7d, 0x1e, 0x94, 0xd7, 0x28, 0xe4, 0x81, 0xeb, 0xec, + 0xa7, 0x3b, 0xce, 0x3d, 0x98, 0x59, 0xc1, 0x46, 0x46, 0xda, 0x93, 0x6c, 0xa5, 0xdf, 0x1a, 0xe7, + 0x93, 0x95, 0x75, 0x8b, 0x9c, 0x5d, 0xe2, 0xf0, 0x71, 0x38, 0x7e, 0xbf, 0x2c, 0x46, 0xe5, 0x13, + 0x30, 0x26, 0xd5, 0x13, 0xbc, 0x50, 0x3c, 0x28, 0xb2, 0xc8, 0x05, 0x5a, 0x5c, 0x40, 0x09, 0xf3, + 0x2f, 0xec, 0x9b, 0x3f, 0x56, 0x60, 0x8a, 0x9c, 0xda, 0x97, 0x3d, 0xd7, 0xc5, 0x66, 0x84, 0x2d, + 0xf9, 0x64, 0xaf, 0x14, 0x9e, 0xec, 0x47, 0xc8, 0x33, 0xb4, 0x00, 0xa2, 0xc0, 0x70, 0x43, 0xdf, + 0x0b, 0x22, 0x56, 0xc0, 0x31, 0xbd, 0x78, 0x7d, 0x58, 0xf5, 0x05, 0xa1, 0x2e, 0xf1, 0x40, 0x27, + 0x60, 0xa2, 0x6b, 0x58, 0x56, 0xc0, 0xea, 0x38, 0x6a, 0x3a, 0x6f, 0x35, 0x5f, 0x03, 0x95, 0xa8, + 0xa9, 0x63, 0x93, 0x75, 0xc6, 0x76, 0x3b, 0x43, 0xf5, 0x46, 0x10, 0x92, 0x28, 0x6a, 0x24, 0x33, + 0x68, 0x9b, 0x30, 0x46, 0x6b, 0x36, 0x66, 0xa0, 0x4e, 0xfe, 0xa6, 0x47, 0xd3, 0x06, 0xcc, 0x11, + 0x40, 0x2f, 0x57, 0x55, 0x41, 0xc7, 0x61, 0x56, 0x7c, 0x49, 0x6c, 0xae, 0x96, 0x64, 0x02, 0x59, + 0x7f, 0xb5, 0xac, 0xad, 0x42, 0x2d, 0x31, 0x03, 0x9a, 0x06, 0x78, 0xe8, 0x47, 0xa9, 0x1c, 0x80, + 0x89, 0x87, 0x7e, 0x74, 0x77, 0xe9, 0xbe, 0xaa, 0xf0, 0xdf, 0xef, 0x2c, 0xdd, 0x57, 0x4b, 0x48, + 0x85, 0xc9, 0x87, 0x7e, 0xd4, 0x0a, 0xbc, 0x47, 0x76, 0xd7, 0x8e, 0xf6, 0xd5, 0xb2, 0xf6, 0x77, + 0x0a, 0x71, 0xf1, 0xcd, 0xb8, 0x43, 0xd6, 0x4f, 0x6a, 0xe9, 0x50, 0x8e, 0xa2, 0xff, 0x4c, 0x19, + 0x31, 0x8c, 0x46, 0x77, 0x33, 0x75, 0x48, 0xa5, 0x61, 0xea, 0x90, 0xd8, 0xf2, 0x93, 0x5b, 0x96, + 0x94, 0x5d, 0xac, 0xca, 0x03, 0x32, 0xa9, 0xbf, 0x57, 0x86, 0x13, 0xb4, 0x33, 0x6b, 0x6e, 0xe8, + 0x63, 0x93, 0xf5, 0x67, 0x3d, 0xf2, 0x02, 0xdc, 0xfc, 0xfa, 0x63, 0xec, 0x0c, 0x1b, 0x50, 0x75, + 0xbc, 0x8e, 0xdc, 0x91, 0x97, 0x72, 0x3b, 0x72, 0x44, 0xe4, 0x5d, 0xaf, 0x43, 0xfb, 0x45, 0xd9, + 0xf2, 0x86, 0x5e, 0x71, 0xd8, 0x8f, 0xe6, 0x2f, 0x94, 0xc1, 0x31, 0x14, 0xba, 0x06, 0x75, 0x5e, + 0xf9, 0x60, 0xa6, 0xa5, 0x0f, 0xd3, 0x87, 0x07, 0xf3, 0xc0, 0x4a, 0x1f, 0x68, 0x49, 0x12, 0x2f, + 0x8e, 0xa0, 0xb5, 0x48, 0xf7, 0xa5, 0xca, 0x28, 0xa9, 0xce, 0xa8, 0x3c, 0x54, 0x9d, 0x51, 0x52, + 0x22, 0x95, 0x80, 0xb2, 0x53, 0x79, 0x6c, 0x50, 0x3d, 0x84, 0x88, 0x19, 0x27, 0xb2, 0xf7, 0xe6, + 0x3e, 0x00, 0x35, 0xce, 0x63, 0x2f, 0x9d, 0xf2, 0x29, 0x8e, 0xcf, 0xbb, 0xb0, 0xa1, 0x90, 0xe9, + 0xcd, 0x08, 0xd8, 0xc4, 0x0b, 0xf5, 0x0a, 0x9b, 0x79, 0xa1, 0xf6, 0xa7, 0x25, 0x98, 0x5b, 0x8a, + 0xa3, 0xed, 0xd5, 0x47, 0xe6, 0xb6, 0xe1, 0x76, 0xb0, 0x8e, 0x43, 0xdf, 0x73, 0x43, 0x8c, 0x9e, + 0x83, 0x49, 0xc3, 0x34, 0x71, 0x18, 0xf2, 0x94, 0x16, 0xab, 0xdd, 0xa9, 0x33, 0x18, 0x4b, 0x52, + 0xcd, 0xc1, 0x78, 0x68, 0x7a, 0x7e, 0x52, 0xc3, 0x43, 0x1b, 0x74, 0x81, 0x0c, 0x02, 0x4f, 0xa4, + 0x82, 0x59, 0x03, 0xbd, 0x00, 0xb3, 0xf4, 0x47, 0xdb, 0xc2, 0xa1, 0x19, 0xd8, 0x3e, 0x39, 0x81, + 0xb0, 0x94, 0xa7, 0xae, 0xd2, 0x0f, 0x2b, 0x29, 0x1c, 0xad, 0x43, 0x95, 0x67, 0xd8, 0x59, 0xbe, + 0xb3, 0xbe, 0xf8, 0x5a, 0xee, 0x80, 0xe4, 0x29, 0x2e, 0x72, 0x78, 0x21, 0x2f, 0x4a, 0x12, 0x8c, + 0x9a, 0x37, 0x60, 0x2a, 0xf3, 0x69, 0xa4, 0xa2, 0xa4, 0x1f, 0x29, 0xd0, 0xa0, 0x23, 0x43, 0x44, + 0x72, 0x36, 0xeb, 0x38, 0xa2, 0x76, 0x68, 0x7e, 0x4b, 0x91, 0xd3, 0x30, 0xe3, 0xa9, 0xbd, 0xea, + 0x8b, 0x57, 0x86, 0xd6, 0x5b, 0x67, 0x74, 0x4f, 0x27, 0x39, 0x9f, 0x6e, 0xa1, 0xbf, 0x09, 0x6a, + 0x6f, 0xca, 0x07, 0x9d, 0x80, 0x52, 0xe2, 0x46, 0xb4, 0x7a, 0xa8, 0x75, 0x47, 0x2f, 0xf9, 0x8f, + 0x79, 0x7f, 0x8b, 0x9a, 0xd2, 0xf1, 0x82, 0x05, 0xdb, 0x49, 0x5b, 0x73, 0xe0, 0xb4, 0x7c, 0x27, + 0xb2, 0x1e, 0xfb, 0x2c, 0x87, 0xcf, 0x81, 0xc4, 0xc9, 0x92, 0x2b, 0x18, 0xb1, 0x2b, 0xd7, 0xf4, + 0xba, 0xb8, 0x5e, 0x61, 0xf3, 0x4a, 0x15, 0x28, 0xd8, 0xb5, 0x7c, 0xcf, 0xe6, 0x5b, 0x6f, 0x4d, + 0x9f, 0xe1, 0xf0, 0x55, 0x0e, 0xd6, 0xfe, 0x55, 0x81, 0x49, 0x59, 0x1c, 0x19, 0x4f, 0xd9, 0x79, + 0x9f, 0xa6, 0x85, 0xd1, 0x67, 0x01, 0x85, 0xa2, 0x3b, 0xed, 0xc4, 0x5b, 0xcb, 0x7d, 0x8a, 0xe7, + 0xfa, 0x59, 0x42, 0x9f, 0x0d, 0x7b, 0x20, 0x21, 0x3a, 0x0b, 0x80, 0x1f, 0xf9, 0x36, 0x4b, 0x41, + 0xd3, 0xb9, 0x52, 0xd6, 0x25, 0x88, 0xf6, 0xdb, 0x0a, 0x9c, 0x94, 0xdc, 0x71, 0xd9, 0xeb, 0xfa, + 0x0e, 0x8e, 0xf0, 0x2d, 0xc7, 0xdb, 0x6b, 0xbe, 0x91, 0x7a, 0xe4, 0x22, 0x4c, 0x9a, 0x86, 0xe3, + 0x6c, 0x1a, 0xe6, 0x0e, 0xed, 0x28, 0xdb, 0x86, 0x67, 0x0e, 0x0f, 0xe6, 0xeb, 0xcb, 0x1c, 0x4e, + 0xba, 0x58, 0x17, 0x48, 0xc4, 0x7d, 0xe4, 0x65, 0x24, 0xb9, 0x92, 0x52, 0xfa, 0x5c, 0x49, 0xfd, + 0x48, 0x81, 0x63, 0x92, 0x2e, 0x6b, 0xae, 0x1d, 0x51, 0x3d, 0xee, 0x65, 0x96, 0x30, 0x62, 0x45, + 0x49, 0x07, 0x56, 0xaa, 0x15, 0x47, 0xdb, 0x44, 0x7e, 0x85, 0x7c, 0x24, 0x86, 0x6d, 0x4a, 0x93, + 0xbf, 0x4c, 0x03, 0x93, 0x74, 0x0e, 0xb7, 0xa4, 0x9d, 0x20, 0xe5, 0x43, 0x77, 0x02, 0xc2, 0x83, + 0xc0, 0xc8, 0x16, 0x18, 0x62, 0x33, 0x0e, 0x70, 0x32, 0xac, 0x55, 0xb6, 0x05, 0xae, 0x53, 0x28, + 0xc1, 0xab, 0x31, 0x84, 0x8d, 0xc0, 0xd1, 0x7e, 0xa1, 0xc0, 0x85, 0xe5, 0x00, 0x5b, 0x64, 0x70, + 0x0d, 0xe7, 0x53, 0x38, 0xb0, 0xb7, 0xa4, 0x7b, 0x2b, 0xb9, 0x2b, 0x52, 0xae, 0xf5, 0x1a, 0x08, + 0x17, 0x95, 0x7a, 0x43, 0x37, 0x1b, 0x4e, 0x44, 0x84, 0x00, 0x47, 0x21, 0x7d, 0xca, 0x16, 0xf0, + 0x96, 0x7a, 0x0b, 0x78, 0x11, 0x8c, 0x39, 0xb6, 0xbb, 0xc3, 0x57, 0x4c, 0xfa, 0xfb, 0x43, 0xe8, + 0xea, 0x77, 0x15, 0xb8, 0xd2, 0xb7, 0xab, 0xc3, 0x79, 0x90, 0x9d, 0xef, 0x41, 0x6b, 0xb2, 0x07, + 0xd9, 0xcd, 0x4b, 0x42, 0xfd, 0xb3, 0x00, 0x36, 0x15, 0xb9, 0x65, 0xf3, 0xa2, 0xd5, 0x9a, 0x2e, + 0x41, 0xb4, 0x2f, 0x97, 0xe0, 0x24, 0xd3, 0x05, 0x5b, 0xa9, 0x76, 0x21, 0x3d, 0x9e, 0x7e, 0x45, + 0x5a, 0x58, 0x5f, 0x80, 0xd9, 0x2d, 0xdb, 0x89, 0xe8, 0x96, 0xd6, 0xc3, 0x4e, 0x65, 0x1f, 0xd6, + 0x12, 0x38, 0x39, 0x19, 0x0a, 0xe4, 0x30, 0x8c, 0x79, 0xb9, 0x58, 0x4d, 0x9f, 0xe4, 0x88, 0x14, + 0x86, 0x2e, 0xc1, 0x0c, 0x7e, 0x64, 0x3a, 0xb1, 0x85, 0xdb, 0x74, 0x56, 0xf1, 0x32, 0x85, 0xaa, + 0x3e, 0xcd, 0xc1, 0xab, 0x0c, 0xda, 0x34, 0x44, 0x5f, 0x3e, 0x0d, 0x60, 0x26, 0x2a, 0xf2, 0x15, + 0xfe, 0xa3, 0xf9, 0x2b, 0x3c, 0xbb, 0xe6, 0x3b, 0xda, 0x31, 0x1d, 0x77, 0xec, 0x30, 0xc2, 0x01, + 0xb6, 0x74, 0x89, 0x97, 0xf6, 0x0d, 0x25, 0xb9, 0x1e, 0x65, 0x9b, 0x2b, 0xed, 0xbf, 0x14, 0x58, + 0x3a, 0x23, 0xce, 0x48, 0x74, 0x03, 0x2a, 0xdc, 0x01, 0x87, 0xbf, 0xd7, 0x16, 0x14, 0xda, 0x6f, + 0xf5, 0x68, 0xb3, 0xec, 0x59, 0x38, 0x33, 0x31, 0x95, 0xec, 0xc4, 0x44, 0x17, 0x60, 0xda, 0xf4, + 0x2c, 0xdc, 0x36, 0xb7, 0x0d, 0xc7, 0xc1, 0x6e, 0x47, 0x6c, 0xa1, 0x53, 0x04, 0xba, 0x2c, 0x80, + 0x19, 0xe5, 0xcb, 0x7d, 0x96, 0x93, 0x0f, 0x14, 0x98, 0xd7, 0xb3, 0x57, 0xc8, 0xf4, 0xba, 0x8c, + 0xd9, 0x8e, 0x45, 0x47, 0xef, 0x66, 0x96, 0x96, 0xa1, 0x6c, 0x32, 0x64, 0x75, 0x50, 0xba, 0x7b, + 0x7e, 0x51, 0x81, 0x73, 0x79, 0x7a, 0x30, 0x08, 0x3f, 0xe1, 0x3e, 0x51, 0xc2, 0x7b, 0x5e, 0x8c, + 0xeb, 0x09, 0x28, 0x79, 0x6c, 0x53, 0xae, 0xb2, 0x4d, 0xf9, 0xc1, 0x1d, 0xbd, 0xe4, 0xed, 0x68, + 0x3f, 0x02, 0x80, 0xf5, 0xfd, 0x30, 0xc2, 0x5d, 0x9a, 0xc0, 0x90, 0x5c, 0xe2, 0xdf, 0x93, 0xb8, + 0x78, 0x09, 0x2a, 0x7e, 0xe0, 0x91, 0xc0, 0x8c, 0x0b, 0xbe, 0x94, 0x3f, 0xd6, 0x09, 0x9b, 0x85, + 0x16, 0x43, 0xd7, 0x05, 0x1d, 0x7a, 0x13, 0xca, 0xfe, 0xa2, 0xdf, 0x37, 0xd9, 0x26, 0x93, 0x2f, + 0xb6, 0xd8, 0x52, 0xd4, 0x5a, 0x6c, 0xe9, 0x84, 0x10, 0xdd, 0x87, 0x8a, 0x17, 0x6c, 0xda, 0x91, + 0xb5, 0xc9, 0x0b, 0xaa, 0x06, 0xaa, 0xf0, 0x80, 0xa0, 0xaf, 0xdc, 0x64, 0x43, 0xc0, 0x1b, 0xba, + 0x60, 0x42, 0xb6, 0xee, 0x3d, 0x23, 0x70, 0xc5, 0xd9, 0x94, 0x35, 0x9a, 0xff, 0xa6, 0x80, 0x40, + 0x45, 0x56, 0x7a, 0xf1, 0x9e, 0xc4, 0x1f, 0xac, 0xf7, 0xaf, 0x0f, 0x29, 0x7a, 0x41, 0x1e, 0x5a, + 0x7a, 0x52, 0xd6, 0x67, 0x38, 0xcb, 0xe4, 0xca, 0xeb, 0x0b, 0x30, 0x7b, 0x04, 0x8b, 0xcc, 0x04, + 0x3f, 0xf0, 0x3a, 0x81, 0x30, 0x78, 0x59, 0x4f, 0xda, 0x34, 0xf5, 0x68, 0x3c, 0xb2, 0xbb, 0x71, + 0x97, 0x1a, 0xb3, 0xac, 0x8b, 0x26, 0xa1, 0xda, 0x8c, 0xb7, 0xb6, 0xb0, 0x58, 0x68, 0xca, 0x7a, + 0xd2, 0x26, 0x67, 0x71, 0x56, 0xec, 0xc6, 0xf7, 0x79, 0xde, 0x6a, 0x2e, 0x00, 0x31, 0x31, 0x59, + 0xaa, 0x92, 0xc3, 0x6f, 0x9b, 0x84, 0xee, 0x42, 0xee, 0x74, 0x02, 0x26, 0x91, 0x7d, 0xd8, 0xfc, + 0xfa, 0x04, 0x54, 0xf8, 0xd8, 0x12, 0x4d, 0x76, 0x71, 0x10, 0x92, 0xe0, 0x81, 0xad, 0x93, 0xa2, + 0x89, 0x4e, 0x42, 0x65, 0xd7, 0x0c, 0xdb, 0x01, 0xde, 0xe2, 0xd3, 0x74, 0x62, 0xd7, 0x0c, 0x75, + 0xbc, 0x45, 0x0e, 0x31, 0xb1, 0x1f, 0xd9, 0x5d, 0xdc, 0xee, 0x86, 0x4c, 0x47, 0x76, 0x88, 0xd9, + 0xa0, 0xc0, 0x7b, 0xeb, 0x7a, 0x95, 0x7d, 0xbe, 0x17, 0xa2, 0x8f, 0x81, 0x1a, 0x87, 0x38, 0x68, + 0x9b, 0x7e, 0xdc, 0x16, 0x14, 0x40, 0x29, 0x66, 0x0f, 0x0f, 0xe6, 0xa7, 0x36, 0x42, 0x1c, 0x2c, + 0xb7, 0x36, 0x1e, 0x32, 0xb2, 0x29, 0x82, 0xba, 0xec, 0xc7, 0x0f, 0x19, 0xed, 0x27, 0x01, 0x85, + 0x74, 0x34, 0x32, 0xd4, 0x75, 0x4a, 0x4d, 0xcb, 0x67, 0xd9, 0x58, 0xa5, 0xf4, 0x33, 0x0c, 0x3d, + 0xe5, 0x70, 0x06, 0x20, 0x8c, 0x0c, 0x1a, 0x7b, 0x19, 0x51, 0x63, 0x92, 0xda, 0xa2, 0xc6, 0x21, + 0x4b, 0xf4, 0xc1, 0x4c, 0xe0, 0x90, 0x23, 0x7b, 0xdb, 0x8c, 0x83, 0xc6, 0x14, 0x2d, 0x9a, 0xae, + 0x31, 0xc8, 0x72, 0x4c, 0xb7, 0x07, 0x37, 0xee, 0xb6, 0x3b, 0x5e, 0xe0, 0xc5, 0x91, 0xed, 0xe2, + 0xc6, 0x34, 0x65, 0x30, 0xe9, 0xc6, 0xdd, 0xdb, 0x02, 0x46, 0x86, 0xc4, 0xf5, 0xb6, 0x6c, 0x07, + 0x37, 0x66, 0xd8, 0x90, 0xb0, 0x16, 0x7a, 0x09, 0x8e, 0x45, 0x9e, 0xd7, 0xee, 0x1a, 0xee, 0x7e, + 0xdb, 0xf3, 0xb1, 0xdb, 0x26, 0xd0, 0xb0, 0xa1, 0xd2, 0xad, 0x43, 0x8d, 0x3c, 0xef, 0x9e, 0xe1, + 0xee, 0x3f, 0xf0, 0xb1, 0x7b, 0x8b, 0xc0, 0xd1, 0x79, 0xa8, 0x10, 0x59, 0xa6, 0x1f, 0x37, 0x66, + 0x69, 0x07, 0x69, 0x02, 0xe4, 0x7e, 0x4c, 0x7a, 0xa7, 0x4f, 0xb8, 0x31, 0xe9, 0x14, 0xd1, 0xb7, + 0xe3, 0xb5, 0xc5, 0x68, 0x21, 0x3a, 0x26, 0xb5, 0x8e, 0xf7, 0x29, 0x3e, 0x5e, 0x57, 0x40, 0xf5, + 0x7c, 0x1c, 0xd0, 0x42, 0x9f, 0x36, 0x33, 0x45, 0xe3, 0x18, 0x8b, 0x81, 0x13, 0x38, 0x33, 0x19, + 0x7a, 0x16, 0x6a, 0xdb, 0x5e, 0x18, 0xb5, 0x5d, 0xa3, 0x8b, 0x1b, 0x73, 0x14, 0xa7, 0x4a, 0x00, + 0xf7, 0x8d, 0x2e, 0x26, 0x71, 0x86, 0x11, 0x98, 0xdb, 0x8d, 0xe3, 0x2c, 0xce, 0x20, 0xbf, 0x25, + 0x53, 0x75, 0x8d, 0x47, 0x8d, 0x13, 0xb2, 0xa9, 0xee, 0x19, 0x8f, 0x48, 0xf4, 0xe1, 0xdb, 0x56, + 0xe3, 0x24, 0x55, 0x9d, 0x4d, 0x79, 0x72, 0xe4, 0xf6, 0x6d, 0x0b, 0x9d, 0x86, 0x31, 0x9f, 0x7c, + 0x6b, 0xd0, 0x6f, 0xd5, 0xc3, 0x83, 0xf9, 0xb1, 0x16, 0xf9, 0x48, 0xa1, 0x6c, 0x8e, 0xd8, 0x5e, + 0x60, 0x47, 0xfb, 0x8d, 0x53, 0x62, 0x8e, 0xb0, 0x36, 0x0d, 0x69, 0x6c, 0xab, 0xd1, 0x4c, 0x99, + 0x6e, 0x10, 0xa6, 0xb1, 0x6d, 0xa1, 0x79, 0xa8, 0xef, 0x79, 0xc1, 0x0e, 0xe9, 0xa8, 0x65, 0x07, + 0x8d, 0x67, 0x59, 0xbc, 0xc0, 0x41, 0x2b, 0x36, 0xdd, 0xb5, 0xb9, 0xef, 0x10, 0x9f, 0xa2, 0xdd, + 0x3c, 0x4d, 0x91, 0xa6, 0x19, 0x78, 0x83, 0x43, 0xb5, 0x5f, 0x8f, 0x43, 0x95, 0x4c, 0x8a, 0xde, + 0x9d, 0x74, 0x49, 0xac, 0x9a, 0x1f, 0x85, 0x71, 0x31, 0x95, 0xca, 0x85, 0x97, 0x22, 0x82, 0x03, + 0xfd, 0xa1, 0x33, 0x82, 0xe6, 0x0f, 0x4b, 0x30, 0x46, 0xda, 0xd2, 0x3b, 0x8c, 0x5a, 0xe6, 0x1d, + 0xc6, 0x0d, 0x98, 0x20, 0x6e, 0x84, 0x59, 0x22, 0xa2, 0x68, 0x41, 0x4d, 0x78, 0xeb, 0x04, 0x57, + 0xe7, 0x24, 0xc4, 0xf1, 0xe8, 0x89, 0x58, 0x84, 0xbf, 0xbc, 0x85, 0x96, 0xa0, 0xba, 0x85, 0x8d, + 0x28, 0x0e, 0x30, 0x5b, 0x15, 0xa7, 0x8b, 0x9e, 0xb0, 0x08, 0xb6, 0xb7, 0x18, 0xb6, 0x9e, 0x90, + 0x11, 0xeb, 0x76, 0x6d, 0xb7, 0xed, 0x18, 0x11, 0x76, 0x4d, 0xf6, 0x06, 0xab, 0xac, 0x43, 0xd7, + 0x76, 0xef, 0x32, 0x08, 0x71, 0x1f, 0x3b, 0x6c, 0xd3, 0x0c, 0x2e, 0xe6, 0xe9, 0xf4, 0xaa, 0x1d, + 0xd2, 0xfc, 0x31, 0x46, 0x1f, 0x87, 0x9a, 0x65, 0x07, 0xd8, 0xa4, 0xe7, 0x91, 0x4a, 0x9f, 0x44, + 0xc9, 0x8a, 0xc0, 0xd2, 0x53, 0x82, 0xe6, 0xcf, 0xc8, 0x76, 0x45, 0x7a, 0x98, 0x15, 0xa2, 0xf4, + 0x08, 0x69, 0x40, 0xc5, 0xb0, 0x2c, 0xba, 0xb4, 0xb2, 0xb5, 0x49, 0x34, 0xb3, 0xe2, 0xcb, 0x23, + 0x8a, 0x27, 0x7c, 0x45, 0xb7, 0xd9, 0x12, 0x2b, 0x9a, 0xe8, 0x4d, 0xa8, 0x84, 0x51, 0x80, 0x8d, + 0xae, 0x48, 0x36, 0x3c, 0xdf, 0xdf, 0xac, 0xeb, 0x14, 0x59, 0x17, 0x44, 0xcd, 0x73, 0x30, 0xc1, + 0x40, 0x45, 0xee, 0xa0, 0xbd, 0x0f, 0x15, 0x3e, 0x16, 0x08, 0xc1, 0x34, 0x4f, 0x3b, 0x72, 0x88, + 0xfa, 0x0c, 0x9a, 0x81, 0xfa, 0x3b, 0x38, 0xdc, 0x16, 0x00, 0x05, 0x4d, 0x03, 0xdc, 0xbc, 0xbb, + 0x2a, 0xda, 0x34, 0x0d, 0x79, 0xd7, 0x33, 0x0d, 0x47, 0x40, 0xca, 0x34, 0x81, 0xe9, 0x05, 0xa2, + 0x3d, 0x46, 0x58, 0xbc, 0x1d, 0xdb, 0xa6, 0x00, 0x8c, 0x6b, 0xdf, 0x57, 0xa0, 0xda, 0x12, 0x7b, + 0xd2, 0x1c, 0x8c, 0x87, 0x91, 0x11, 0x89, 0xf3, 0x35, 0x6b, 0x10, 0xa8, 0xe5, 0xd9, 0x6e, 0x47, + 0x64, 0x3b, 0x68, 0x23, 0xb3, 0xb7, 0x11, 0x23, 0x97, 0xa4, 0xbd, 0xed, 0x34, 0xd4, 0x4c, 0x7e, + 0x46, 0x60, 0x1b, 0xd5, 0x98, 0x9e, 0x02, 0xd8, 0x69, 0x3b, 0x32, 0x1c, 0xea, 0x56, 0x63, 0x3a, + 0x6b, 0x50, 0x29, 0xd8, 0x31, 0xd8, 0x53, 0xc8, 0x31, 0x9d, 0x35, 0x34, 0x17, 0x66, 0xd9, 0xad, + 0xc6, 0x3b, 0x76, 0xb4, 0xcd, 0x52, 0x64, 0xe1, 0x28, 0x6f, 0x71, 0x16, 0xa0, 0xce, 0xd2, 0x69, + 0x61, 0xdb, 0xdf, 0xc9, 0xbc, 0x74, 0x12, 0xf9, 0xb6, 0x50, 0x07, 0x8e, 0xd1, 0xda, 0x09, 0xb5, + 0x03, 0x05, 0x66, 0x1f, 0xc4, 0xd1, 0x83, 0x2d, 0x9a, 0xdd, 0x14, 0x6f, 0xc7, 0xfa, 0xe4, 0x13, + 0x47, 0xc8, 0xcc, 0x4b, 0xcf, 0x73, 0x88, 0xc1, 0x26, 0xd2, 0x37, 0x79, 0xfc, 0xa1, 0xdd, 0x58, + 0xfa, 0xd0, 0x6e, 0x0e, 0xc6, 0xb7, 0x1c, 0xa3, 0x13, 0x52, 0x1b, 0x55, 0x74, 0xd6, 0xa0, 0xc9, + 0x31, 0xf1, 0xae, 0xad, 0x9d, 0x4d, 0x0d, 0xaa, 0xc9, 0x87, 0x16, 0x7f, 0xbe, 0x98, 0x3c, 0x14, + 0xab, 0x48, 0x0f, 0xc5, 0xb4, 0x9f, 0x28, 0x70, 0x2c, 0xa7, 0xb2, 0x0c, 0xad, 0x00, 0xb0, 0xd0, + 0x58, 0xba, 0xf5, 0x90, 0x96, 0x8d, 0x38, 0xdc, 0xee, 0xa9, 0x49, 0xa3, 0x41, 0x33, 0x4b, 0x2b, + 0x47, 0xe2, 0x27, 0xb1, 0xc6, 0x66, 0xec, 0x5a, 0x0e, 0x4e, 0x4b, 0x53, 0xa9, 0x35, 0x6e, 0x52, + 0xe0, 0xda, 0x0a, 0x89, 0x64, 0xe8, 0x2f, 0x2b, 0xcd, 0xb9, 0xf0, 0xdb, 0x62, 0x96, 0x73, 0xb9, + 0x0e, 0x73, 0x01, 0x36, 0x6d, 0xdf, 0xc6, 0x6e, 0xd4, 0x96, 0x8e, 0xc2, 0xcc, 0x34, 0x28, 0xf9, + 0xd6, 0x12, 0x67, 0x62, 0xed, 0x3e, 0x40, 0x5a, 0xc0, 0xc6, 0x1e, 0xf3, 0x92, 0x5f, 0xf2, 0x0b, + 0x58, 0x06, 0x21, 0x07, 0x68, 0x29, 0x8f, 0x44, 0x56, 0x0b, 0xee, 0xd1, 0xe2, 0x90, 0xbe, 0x64, + 0x59, 0x81, 0xf6, 0x55, 0x05, 0x4e, 0x11, 0x86, 0x6c, 0x00, 0x79, 0x89, 0xae, 0x38, 0x8b, 0xa1, + 0x37, 0xb3, 0x69, 0xbb, 0xe1, 0x2b, 0xf7, 0x78, 0xff, 0x86, 0x77, 0x17, 0x72, 0xa6, 0x68, 0xa6, + 0x8a, 0xf0, 0x92, 0xbd, 0x54, 0x93, 0xd7, 0x60, 0x82, 0x57, 0xfb, 0x29, 0xc3, 0x55, 0xfb, 0x71, + 0xf4, 0x51, 0x54, 0xf8, 0xfb, 0x52, 0xf2, 0xf6, 0xa3, 0xdf, 0x09, 0x75, 0x94, 0x9a, 0xe2, 0x1b, + 0xd0, 0x0c, 0xed, 0x8e, 0x8b, 0x2d, 0x7e, 0x3c, 0x8f, 0xf6, 0xdb, 0x47, 0x32, 0x1e, 0x27, 0x19, + 0xc6, 0x1a, 0x47, 0x48, 0xc6, 0x1a, 0x5d, 0x83, 0x63, 0xbb, 0x5c, 0x8f, 0xb6, 0x74, 0xc0, 0x66, + 0xe9, 0x10, 0xb4, 0x7b, 0x44, 0x45, 0x32, 0x61, 0x02, 0xaa, 0x26, 0x4b, 0x85, 0xb5, 0x2d, 0xb2, + 0xb8, 0xb1, 0x65, 0x5d, 0x95, 0x3f, 0xac, 0x90, 0x75, 0x8e, 0x9e, 0xf3, 0x45, 0xd6, 0x8c, 0xa1, + 0xb2, 0x8d, 0x6f, 0x3a, 0x05, 0x53, 0xc4, 0x6c, 0xaa, 0x62, 0xa2, 0x37, 0x55, 0x41, 0x36, 0x66, + 0x9e, 0x4e, 0xa8, 0xb0, 0xa8, 0x99, 0xb5, 0xb4, 0x6f, 0x2b, 0x70, 0x9c, 0x0c, 0x08, 0x5b, 0xa7, + 0xa8, 0x6b, 0x6d, 0xf8, 0x44, 0xce, 0xe3, 0x0f, 0x66, 0x32, 0x8b, 0x4a, 0xf2, 0x2c, 0x1a, 0xe1, + 0xd6, 0xf7, 0xbf, 0x32, 0x0b, 0x1e, 0x77, 0xd7, 0xe6, 0xf9, 0xf4, 0xa8, 0x2a, 0x5d, 0x42, 0x28, + 0x99, 0x4b, 0x88, 0xe6, 0x5f, 0x27, 0xe7, 0xca, 0x4f, 0xa6, 0x65, 0x14, 0x4c, 0xff, 0x8b, 0xb9, + 0xfa, 0x1f, 0x59, 0x58, 0xd3, 0x57, 0xad, 0x64, 0xc7, 0x70, 0x30, 0x09, 0xcb, 0x1f, 0x89, 0x1b, + 0xd3, 0x14, 0x80, 0x2e, 0x83, 0xca, 0xcf, 0xe3, 0xa9, 0xab, 0xb0, 0x65, 0x63, 0x9a, 0x1d, 0xc5, + 0x13, 0x0f, 0xb9, 0x02, 0xaa, 0xe1, 0x04, 0xd8, 0xb0, 0xf6, 0xdb, 0x01, 0x7f, 0xc7, 0x42, 0xc7, + 0xbb, 0xaa, 0xcf, 0x70, 0xb8, 0x78, 0xde, 0x42, 0xeb, 0x7a, 0x52, 0x8d, 0xd6, 0xb1, 0xe1, 0x34, + 0xef, 0xa7, 0xdd, 0xee, 0xb3, 0xe4, 0xe7, 0x69, 0x53, 0xca, 0xd3, 0xa6, 0x79, 0x41, 0x18, 0xe8, + 0x34, 0xd4, 0x92, 0xf5, 0x59, 0xac, 0x4a, 0x09, 0x40, 0x7b, 0x03, 0x66, 0x6f, 0xd9, 0x41, 0x18, + 0xdd, 0x35, 0xc2, 0x68, 0x99, 0x6d, 0x09, 0x74, 0x2f, 0xde, 0x22, 0x40, 0xfe, 0x7e, 0x9b, 0x35, + 0x68, 0x06, 0xd0, 0x08, 0x23, 0xfe, 0xbe, 0x93, 0xfe, 0xd6, 0xfe, 0x49, 0x81, 0x63, 0xfc, 0xa4, + 0x2a, 0xd5, 0xb9, 0xb0, 0xb3, 0x0f, 0x36, 0x1c, 0x6c, 0xb5, 0x37, 0xbd, 0x47, 0xc2, 0xa8, 0x0c, + 0x72, 0xd3, 0x7b, 0x44, 0xd6, 0xc2, 0xc0, 0xd8, 0x6b, 0x07, 0x1e, 0x2b, 0xf3, 0xe2, 0x06, 0xad, + 0x07, 0xc6, 0x9e, 0xce, 0x41, 0xcd, 0x0f, 0x14, 0x28, 0x13, 0x54, 0x29, 0xd6, 0x52, 0xb2, 0xb1, + 0xd6, 0x1c, 0x8c, 0xd3, 0x77, 0xfe, 0xc2, 0xff, 0x68, 0x63, 0x04, 0xff, 0xeb, 0x2d, 0x6c, 0x9f, + 0xcc, 0xbd, 0xf7, 0xfd, 0xb5, 0x02, 0xc7, 0x75, 0xbc, 0x15, 0xe0, 0x70, 0x3b, 0x5b, 0xf4, 0xd9, + 0x7c, 0x75, 0x40, 0x80, 0x3d, 0x07, 0xe3, 0xec, 0xea, 0xba, 0xc4, 0xd2, 0x03, 0xec, 0xe6, 0xfa, + 0xed, 0xc7, 0xac, 0xbe, 0x24, 0x86, 0x20, 0xa7, 0x50, 0x2f, 0x8e, 0xc4, 0xa1, 0x9d, 0x37, 0x9b, + 0xef, 0x8a, 0xa1, 0x6e, 0x41, 0x9d, 0x06, 0xff, 0xed, 0x2d, 0x2f, 0x76, 0x2d, 0x7e, 0x66, 0xb8, + 0x96, 0x3b, 0x1f, 0x72, 0xbb, 0xc4, 0x0e, 0x10, 0x40, 0x79, 0xdc, 0x22, 0x2c, 0xae, 0xda, 0x90, + 0xde, 0xe1, 0xa2, 0x13, 0xbc, 0x04, 0x8c, 0xdd, 0x7f, 0x5b, 0x78, 0xcb, 0x76, 0xb1, 0xa5, 0x3e, + 0x83, 0xe6, 0x78, 0xd5, 0x0e, 0x81, 0xf3, 0x25, 0x5b, 0x55, 0x32, 0x50, 0x2e, 0x86, 0x5d, 0x7e, + 0x27, 0x50, 0xa9, 0xee, 0x4f, 0x2d, 0x5f, 0xfd, 0x4a, 0x0d, 0x6a, 0xe9, 0x55, 0xe5, 0x09, 0x40, + 0x49, 0x43, 0x96, 0x75, 0x1e, 0xe6, 0x13, 0x38, 0x2f, 0x15, 0x4a, 0x5f, 0x4a, 0xd3, 0xe7, 0x35, + 0xaa, 0x82, 0x2e, 0xc0, 0x73, 0x59, 0xa4, 0xec, 0xbb, 0x63, 0x86, 0x56, 0x42, 0xf3, 0xf0, 0x6c, + 0x82, 0x76, 0xf4, 0x61, 0xa7, 0x8a, 0xd1, 0x19, 0x38, 0x95, 0x8b, 0x70, 0x17, 0x6f, 0x45, 0xea, + 0x16, 0xba, 0x0a, 0x17, 0x7b, 0x3f, 0xe7, 0x3f, 0x9f, 0x54, 0x3b, 0xe8, 0x0a, 0x5c, 0xe8, 0x8f, + 0x2b, 0xca, 0xdb, 0xb7, 0xd1, 0x75, 0x78, 0xb1, 0x3f, 0x6a, 0xf6, 0xf5, 0xa3, 0x6a, 0xa3, 0x45, + 0x58, 0xe8, 0x4f, 0xf1, 0x20, 0x8e, 0x3a, 0x24, 0x72, 0x16, 0xcf, 0x15, 0xd5, 0xcf, 0xa1, 0x05, + 0xb8, 0x3a, 0x1c, 0xcd, 0x3a, 0x76, 0x23, 0x75, 0x67, 0xb0, 0x8c, 0x35, 0xd7, 0xf4, 0xba, 0xb6, + 0xdb, 0x11, 0x8b, 0x9c, 0xea, 0xa0, 0x57, 0xe0, 0xda, 0x70, 0x34, 0xc9, 0xd3, 0x38, 0xb5, 0x3b, + 0xbc, 0x20, 0xf1, 0xa6, 0x4d, 0x75, 0x91, 0x06, 0x67, 0x0b, 0x68, 0xf8, 0xeb, 0x32, 0xd5, 0x43, + 0xcf, 0xc3, 0xb9, 0x02, 0x9c, 0xe4, 0x3d, 0x98, 0xea, 0x23, 0x0d, 0xce, 0x24, 0x58, 0x3d, 0x35, + 0xce, 0xcc, 0x6d, 0x7e, 0xac, 0xa0, 0xeb, 0xf0, 0x42, 0x82, 0xd3, 0xb7, 0x56, 0x97, 0x51, 0xfc, + 0xa0, 0x84, 0x5e, 0x95, 0x0c, 0x71, 0xb4, 0xda, 0x55, 0x7a, 0x57, 0xbd, 0xe4, 0xba, 0x5e, 0xec, + 0x9a, 0xd8, 0x52, 0xff, 0xbc, 0x84, 0x16, 0xe0, 0x4a, 0xb1, 0x9c, 0x4c, 0xb5, 0x2e, 0xb6, 0xd4, + 0xbf, 0x28, 0xa1, 0x8b, 0x92, 0xdb, 0x17, 0x3d, 0x3e, 0x53, 0xbf, 0x5d, 0x46, 0x97, 0xe1, 0x7c, + 0x3f, 0x3c, 0xfe, 0x2a, 0x4c, 0xfd, 0x4e, 0x19, 0x9d, 0x95, 0x26, 0x40, 0xef, 0x6b, 0x2e, 0xf5, + 0xbb, 0x65, 0x74, 0x5e, 0xb2, 0x7b, 0x6e, 0x74, 0xa1, 0xfe, 0x7e, 0x19, 0x5d, 0x02, 0x2d, 0x83, + 0x94, 0x1b, 0xdd, 0xaa, 0xdf, 0xcb, 0xea, 0x55, 0x1c, 0x7d, 0xaa, 0x7f, 0x50, 0x46, 0x2f, 0x1f, + 0x9d, 0x22, 0xfd, 0x82, 0x44, 0xf5, 0x97, 0xe5, 0x8c, 0x71, 0x32, 0xc5, 0x95, 0xfc, 0xcc, 0x42, + 0xdd, 0xfc, 0x5f, 0x2a, 0x57, 0xbf, 0x21, 0x6e, 0xd1, 0x73, 0x8a, 0x3f, 0xc8, 0xc2, 0x52, 0xf4, + 0xad, 0x67, 0x91, 0x2a, 0x42, 0xe3, 0xbb, 0xa4, 0xaa, 0x10, 0x7f, 0x2c, 0x46, 0x62, 0xaa, 0xa9, + 0xa5, 0xab, 0x7f, 0xa3, 0x24, 0x2f, 0x02, 0xd8, 0xb3, 0x98, 0x53, 0xc9, 0xe3, 0x03, 0xda, 0x96, + 0xc5, 0xf6, 0x7c, 0x7a, 0xe8, 0xf1, 0x09, 0xa3, 0x2a, 0x64, 0xd9, 0x95, 0x3f, 0x25, 0x73, 0xb4, + 0x84, 0x8e, 0xc3, 0xac, 0xfc, 0x85, 0x39, 0x49, 0x19, 0x9d, 0x4c, 0x4a, 0xfc, 0x39, 0x01, 0xf3, + 0x89, 0xb1, 0x5e, 0x21, 0xe9, 0xcc, 0x1d, 0xef, 0xa5, 0x11, 0x53, 0x6f, 0xe2, 0xea, 0x6d, 0xa8, + 0x25, 0xf9, 0x0e, 0x34, 0x0d, 0xc0, 0xb3, 0x0b, 0x2b, 0x76, 0xa0, 0x3e, 0x43, 0xda, 0x6b, 0xee, + 0x26, 0xd9, 0x6d, 0x48, 0x5b, 0x41, 0x33, 0x50, 0x7f, 0x10, 0x47, 0x09, 0xa0, 0x84, 0x6a, 0x30, + 0x7e, 0xd3, 0x26, 0x3f, 0xcb, 0x8b, 0xdf, 0xbc, 0x02, 0x33, 0xe2, 0xbf, 0x9f, 0x88, 0xfb, 0xf9, + 0x30, 0xe7, 0xd1, 0x1e, 0x5a, 0xe8, 0x77, 0x91, 0x94, 0xe2, 0x2d, 0x24, 0x2f, 0xfb, 0x86, 0xc6, + 0xf7, 0x9d, 0xfd, 0xeb, 0x0a, 0xfa, 0xb2, 0x52, 0xf8, 0xb6, 0x0f, 0xbd, 0x3a, 0xd2, 0xb3, 0x2d, + 0xa1, 0xc1, 0xe2, 0x88, 0x54, 0x64, 0xbf, 0x27, 0x5a, 0x14, 0x6c, 0x0d, 0x05, 0x5a, 0x14, 0x60, + 0x0f, 0xd0, 0xa2, 0x98, 0x8a, 0x68, 0xf1, 0x85, 0x82, 0x07, 0x4f, 0x68, 0x18, 0x66, 0x1c, 0x37, + 0x51, 0xe0, 0xfa, 0x48, 0x34, 0x44, 0xfc, 0xe7, 0xf3, 0x9f, 0x50, 0xa1, 0x97, 0x87, 0xe0, 0xc4, + 0x50, 0x13, 0xe1, 0xd7, 0x46, 0x21, 0x21, 0xb2, 0xbf, 0xa3, 0xf4, 0x7f, 0x5d, 0x85, 0x5e, 0x1f, + 0xca, 0x9e, 0x32, 0x49, 0xa2, 0xcc, 0x6b, 0x8f, 0x43, 0x4a, 0x94, 0x8a, 0xf2, 0x9e, 0x61, 0xa1, + 0x61, 0xfa, 0x46, 0x10, 0x13, 0xf9, 0x2f, 0x0d, 0x4f, 0x90, 0x3b, 0x0c, 0x6c, 0x7b, 0x1e, 0x6a, + 0x18, 0x18, 0xea, 0x48, 0xc3, 0x90, 0x90, 0x14, 0x79, 0x20, 0x59, 0x95, 0x86, 0xf5, 0x40, 0x82, + 0x3b, 0xaa, 0x07, 0x72, 0x1a, 0x22, 0x7e, 0x33, 0xfb, 0x68, 0x0b, 0x5d, 0xe9, 0xc7, 0x81, 0xa2, + 0x24, 0xc2, 0x2e, 0x0d, 0x83, 0x4a, 0x64, 0x6c, 0xf7, 0x3e, 0xe3, 0x42, 0x2f, 0xf4, 0x23, 0xe5, + 0x48, 0x89, 0x9c, 0x2b, 0xc3, 0x21, 0x13, 0x49, 0x7b, 0xb9, 0x8f, 0xbb, 0x50, 0x5f, 0xb3, 0xc8, + 0x98, 0x89, 0xcc, 0x85, 0x11, 0x28, 0x88, 0xe0, 0x2f, 0x2a, 0x45, 0x6f, 0xc0, 0xd0, 0x2b, 0xf9, + 0x2f, 0x2b, 0x72, 0x91, 0x13, 0xf9, 0x2f, 0x8f, 0x46, 0xc4, 0x9d, 0x38, 0xef, 0xbd, 0x18, 0x1a, + 0x8e, 0x15, 0x41, 0x1d, 0xe0, 0xc4, 0x05, 0x24, 0xdc, 0x89, 0x73, 0x5f, 0x93, 0x15, 0x38, 0x71, + 0x2e, 0xee, 0x00, 0x27, 0x2e, 0xa2, 0x21, 0xe2, 0x7f, 0xa0, 0x0c, 0xf9, 0xf0, 0x0c, 0xdd, 0x1c, + 0x8a, 0x77, 0x2e, 0x6d, 0xa2, 0xdf, 0x27, 0x9f, 0x88, 0x07, 0xd1, 0xf7, 0x0f, 0x07, 0x3e, 0x61, + 0x43, 0x37, 0x86, 0x13, 0x92, 0x21, 0x4a, 0x34, 0x7c, 0xfd, 0xf1, 0x88, 0x89, 0x6a, 0x7f, 0x3c, + 0xc4, 0x9b, 0x36, 0xf4, 0xc6, 0x50, 0xfc, 0x7b, 0xc9, 0x12, 0xf5, 0x6e, 0x3c, 0x2e, 0x39, 0x51, + 0x70, 0xe7, 0xc8, 0x03, 0x37, 0x94, 0x1f, 0x00, 0xf5, 0x60, 0x25, 0xd2, 0xaf, 0x0e, 0x89, 0xcd, + 0x57, 0xae, 0xec, 0x13, 0xb6, 0x82, 0x95, 0x2b, 0x8b, 0x34, 0x60, 0xe5, 0x3a, 0x82, 0x4c, 0x24, + 0xb9, 0x39, 0xcf, 0xa6, 0x0a, 0x22, 0xc1, 0x23, 0x78, 0x03, 0x56, 0xe4, 0xa3, 0x4f, 0xdf, 0xae, + 0x2b, 0x68, 0xe7, 0xe8, 0x6b, 0x25, 0xf4, 0x52, 0x3f, 0xf2, 0x04, 0x2d, 0x91, 0x76, 0x71, 0x20, + 0xba, 0x10, 0xf6, 0xae, 0xf4, 0x44, 0x08, 0xf5, 0x21, 0xa3, 0xc5, 0x23, 0x82, 0xfd, 0xf3, 0x03, + 0xf1, 0x88, 0xdd, 0x70, 0xcf, 0xeb, 0x1b, 0x54, 0x30, 0xbc, 0x32, 0x4e, 0x22, 0xe2, 0xf2, 0x50, + 0xb8, 0xdc, 0xeb, 0x7a, 0x1e, 0xde, 0x14, 0x78, 0x5d, 0x0f, 0xd6, 0x00, 0xaf, 0x3b, 0x8a, 0x4d, + 0x84, 0x85, 0x39, 0xaf, 0x72, 0xfa, 0xf9, 0x42, 0xe6, 0x2d, 0x4a, 0xff, 0x53, 0x41, 0x1e, 0x3e, + 0x3b, 0x15, 0x74, 0x8f, 0xbc, 0xbb, 0x28, 0xec, 0x61, 0x06, 0x6b, 0x60, 0x0f, 0x7b, 0xb1, 0x99, + 0xb8, 0x2f, 0x29, 0x45, 0x4f, 0x23, 0x0a, 0x36, 0xcc, 0x7c, 0xe4, 0x01, 0x1b, 0x66, 0x21, 0x11, + 0x53, 0xe2, 0x3d, 0xf9, 0x39, 0x00, 0xba, 0x54, 0xcc, 0x22, 0x3b, 0x96, 0x17, 0x06, 0x23, 0x92, + 0x61, 0xfc, 0x6a, 0x9f, 0x9a, 0x76, 0xf4, 0xff, 0x8a, 0x79, 0xe4, 0xa0, 0x27, 0xa2, 0x5f, 0x19, + 0x95, 0x8c, 0x28, 0xf2, 0x9e, 0x5c, 0xe1, 0x86, 0x06, 0x16, 0x8e, 0xf5, 0xef, 0x66, 0x06, 0x91, + 0xc7, 0x5c, 0x39, 0x95, 0xc9, 0x05, 0x31, 0x57, 0x0e, 0xe6, 0x80, 0x98, 0x2b, 0x9f, 0x42, 0x9c, + 0x20, 0x0b, 0xea, 0xb3, 0x0b, 0x4e, 0x90, 0x05, 0xd8, 0x03, 0x4e, 0x90, 0xc5, 0x54, 0x22, 0xf6, + 0x18, 0xaa, 0xc0, 0xb9, 0x20, 0xf6, 0x18, 0x8a, 0x76, 0x40, 0xec, 0x31, 0x2c, 0x0f, 0xa2, 0xef, + 0x5f, 0x8d, 0x52, 0xa5, 0x8c, 0x6e, 0x8d, 0x2e, 0x2f, 0xd7, 0xb2, 0x2b, 0x4f, 0xcc, 0x87, 0xe8, + 0xfe, 0x81, 0x52, 0x58, 0xcb, 0x5c, 0x30, 0xe2, 0x05, 0xd8, 0x03, 0x46, 0xbc, 0x98, 0x8a, 0xad, + 0x1b, 0x61, 0x4e, 0x31, 0x71, 0xff, 0xb4, 0x4d, 0x8a, 0x37, 0x5c, 0xda, 0x26, 0x83, 0xcf, 0x84, + 0xfe, 0xd1, 0xe0, 0x9a, 0x5d, 0xf4, 0xf1, 0x82, 0xfb, 0x92, 0xbe, 0x54, 0x89, 0x46, 0x1f, 0x7b, + 0x4c, 0x6a, 0x32, 0x34, 0x9f, 0x4a, 0x8b, 0xc1, 0xd0, 0x80, 0xb2, 0x29, 0x21, 0x6e, 0x50, 0xd1, + 0x16, 0xe5, 0xfb, 0x7e, 0xce, 0x35, 0x6b, 0x81, 0xa9, 0x8f, 0xe0, 0x0d, 0x30, 0x75, 0x1e, 0x3e, + 0x0f, 0xfa, 0xb2, 0xf7, 0x9b, 0x05, 0x41, 0x5f, 0x16, 0x69, 0x40, 0xd0, 0x77, 0x04, 0x99, 0x1f, + 0x9b, 0x72, 0xef, 0xb2, 0x0a, 0x8e, 0x4d, 0xf9, 0xf7, 0x5e, 0xfd, 0x8f, 0x4d, 0x45, 0x34, 0xbe, + 0xb3, 0x7f, 0xf3, 0x23, 0x3f, 0xfd, 0xe7, 0xb3, 0xcf, 0xfc, 0xed, 0xe1, 0x59, 0xe5, 0xa7, 0x87, + 0x67, 0x95, 0x9f, 0x1f, 0x9e, 0x55, 0x3e, 0xf3, 0xfc, 0x26, 0x0e, 0xa2, 0xfd, 0x85, 0x08, 0x9b, + 0xdb, 0xd7, 0x38, 0xb7, 0x6b, 0xfe, 0x4e, 0xe7, 0x5a, 0xe6, 0xdf, 0xea, 0x6f, 0x4e, 0xd0, 0xe6, + 0x2b, 0xff, 0x13, 0x00, 0x00, 0xff, 0xff, 0x81, 0x08, 0x42, 0xbc, 0x6e, 0x5f, 0x00, 0x00, } func (m *Account) Marshal() (dAtA []byte, err error) { @@ -12423,7 +12006,7 @@ func (m *GroupAddMemberDevice) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } -func (m *DeviceSecret) Marshal() (dAtA []byte, err error) { +func (m *DeviceChainKey) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) @@ -12433,12 +12016,12 @@ func (m *DeviceSecret) Marshal() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *DeviceSecret) MarshalTo(dAtA []byte) (int, error) { +func (m *DeviceChainKey) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *DeviceSecret) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *DeviceChainKey) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int @@ -12462,7 +12045,7 @@ func (m *DeviceSecret) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } -func (m *GroupAddDeviceSecret) Marshal() (dAtA []byte, err error) { +func (m *GroupAddDeviceChainKey) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) @@ -12472,12 +12055,12 @@ func (m *GroupAddDeviceSecret) Marshal() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *GroupAddDeviceSecret) MarshalTo(dAtA []byte) (int, error) { +func (m *GroupAddDeviceChainKey) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *GroupAddDeviceSecret) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *GroupAddDeviceChainKey) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int @@ -18889,7 +18472,7 @@ func (m *PushMemberTokenUpdate) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } -func (m *PushReceive) Marshal() (dAtA []byte, err error) { +func (m *OutOfStoreReceive) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) @@ -18899,12 +18482,12 @@ func (m *PushReceive) Marshal() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *PushReceive) MarshalTo(dAtA []byte) (int, error) { +func (m *OutOfStoreReceive) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *PushReceive) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *OutOfStoreReceive) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int @@ -18916,7 +18499,7 @@ func (m *PushReceive) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } -func (m *PushReceive_Request) Marshal() (dAtA []byte, err error) { +func (m *OutOfStoreReceive_Request) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) @@ -18926,12 +18509,12 @@ func (m *PushReceive_Request) Marshal() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *PushReceive_Request) MarshalTo(dAtA []byte) (int, error) { +func (m *OutOfStoreReceive_Request) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *PushReceive_Request) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *OutOfStoreReceive_Request) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int @@ -18950,7 +18533,7 @@ func (m *PushReceive_Request) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } -func (m *PushReceive_Reply) Marshal() (dAtA []byte, err error) { +func (m *OutOfStoreReceive_Reply) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) @@ -18960,12 +18543,12 @@ func (m *PushReceive_Reply) Marshal() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *PushReceive_Reply) MarshalTo(dAtA []byte) (int, error) { +func (m *OutOfStoreReceive_Reply) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *PushReceive_Reply) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *OutOfStoreReceive_Reply) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int @@ -19013,7 +18596,7 @@ func (m *PushReceive_Reply) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } -func (m *PushSend) Marshal() (dAtA []byte, err error) { +func (m *OutOfStoreSeal) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) @@ -19023,12 +18606,12 @@ func (m *PushSend) Marshal() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *PushSend) MarshalTo(dAtA []byte) (int, error) { +func (m *OutOfStoreSeal) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *PushSend) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *OutOfStoreSeal) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int @@ -19040,7 +18623,7 @@ func (m *PushSend) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } -func (m *PushSend_Request) Marshal() (dAtA []byte, err error) { +func (m *OutOfStoreSeal_Request) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) @@ -19050,12 +18633,12 @@ func (m *PushSend_Request) Marshal() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *PushSend_Request) MarshalTo(dAtA []byte) (int, error) { +func (m *OutOfStoreSeal_Request) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *PushSend_Request) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *OutOfStoreSeal_Request) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int @@ -19064,20 +18647,6 @@ func (m *PushSend_Request) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } - if len(m.GroupMembers) > 0 { - for iNdEx := len(m.GroupMembers) - 1; iNdEx >= 0; iNdEx-- { - { - size, err := m.GroupMembers[iNdEx].MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintProtocoltypes(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x1a - } - } if len(m.GroupPublicKey) > 0 { i -= len(m.GroupPublicKey) copy(dAtA[i:], m.GroupPublicKey) @@ -19095,7 +18664,7 @@ func (m *PushSend_Request) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } -func (m *PushSend_Reply) Marshal() (dAtA []byte, err error) { +func (m *OutOfStoreSeal_Reply) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) @@ -19105,12 +18674,12 @@ func (m *PushSend_Reply) Marshal() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *PushSend_Reply) MarshalTo(dAtA []byte) (int, error) { +func (m *OutOfStoreSeal_Reply) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *PushSend_Reply) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *OutOfStoreSeal_Reply) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int @@ -19119,321 +18688,16 @@ func (m *PushSend_Reply) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } - if len(m.GroupMembers) > 0 { - for iNdEx := len(m.GroupMembers) - 1; iNdEx >= 0; iNdEx-- { - { - size, err := m.GroupMembers[iNdEx].MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintProtocoltypes(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0xa - } - } - return len(dAtA) - i, nil -} - -func (m *PushShareToken) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *PushShareToken) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *PushShareToken) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if m.XXX_unrecognized != nil { - i -= len(m.XXX_unrecognized) - copy(dAtA[i:], m.XXX_unrecognized) - } - return len(dAtA) - i, nil -} - -func (m *PushShareToken_Request) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *PushShareToken_Request) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *PushShareToken_Request) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if m.XXX_unrecognized != nil { - i -= len(m.XXX_unrecognized) - copy(dAtA[i:], m.XXX_unrecognized) - } - if m.Receiver != nil { - { - size, err := m.Receiver.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintProtocoltypes(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x1a - } - if m.Server != nil { - { - size, err := m.Server.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintProtocoltypes(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x12 - } - if len(m.GroupPK) > 0 { - i -= len(m.GroupPK) - copy(dAtA[i:], m.GroupPK) - i = encodeVarintProtocoltypes(dAtA, i, uint64(len(m.GroupPK))) + if len(m.Encrypted) > 0 { + i -= len(m.Encrypted) + copy(dAtA[i:], m.Encrypted) + i = encodeVarintProtocoltypes(dAtA, i, uint64(len(m.Encrypted))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } -func (m *PushShareToken_Reply) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *PushShareToken_Reply) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *PushShareToken_Reply) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if m.XXX_unrecognized != nil { - i -= len(m.XXX_unrecognized) - copy(dAtA[i:], m.XXX_unrecognized) - } - return len(dAtA) - i, nil -} - -func (m *PushSetDeviceToken) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *PushSetDeviceToken) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *PushSetDeviceToken) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if m.XXX_unrecognized != nil { - i -= len(m.XXX_unrecognized) - copy(dAtA[i:], m.XXX_unrecognized) - } - return len(dAtA) - i, nil -} - -func (m *PushSetDeviceToken_Request) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *PushSetDeviceToken_Request) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *PushSetDeviceToken_Request) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if m.XXX_unrecognized != nil { - i -= len(m.XXX_unrecognized) - copy(dAtA[i:], m.XXX_unrecognized) - } - if m.Receiver != nil { - { - size, err := m.Receiver.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintProtocoltypes(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func (m *PushSetDeviceToken_Reply) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *PushSetDeviceToken_Reply) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *PushSetDeviceToken_Reply) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if m.XXX_unrecognized != nil { - i -= len(m.XXX_unrecognized) - copy(dAtA[i:], m.XXX_unrecognized) - } - return len(dAtA) - i, nil -} - -func (m *PushSetServer) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *PushSetServer) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *PushSetServer) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if m.XXX_unrecognized != nil { - i -= len(m.XXX_unrecognized) - copy(dAtA[i:], m.XXX_unrecognized) - } - return len(dAtA) - i, nil -} - -func (m *PushSetServer_Request) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *PushSetServer_Request) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *PushSetServer_Request) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if m.XXX_unrecognized != nil { - i -= len(m.XXX_unrecognized) - copy(dAtA[i:], m.XXX_unrecognized) - } - if m.Server != nil { - { - size, err := m.Server.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintProtocoltypes(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func (m *PushSetServer_Reply) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *PushSetServer_Reply) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *PushSetServer_Reply) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if m.XXX_unrecognized != nil { - i -= len(m.XXX_unrecognized) - copy(dAtA[i:], m.XXX_unrecognized) - } - return len(dAtA) - i, nil -} - func (m *FirstLastCounters) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -20055,7 +19319,7 @@ func (m *GroupAddMemberDevice) Size() (n int) { return n } -func (m *DeviceSecret) Size() (n int) { +func (m *DeviceChainKey) Size() (n int) { if m == nil { return 0 } @@ -20074,7 +19338,7 @@ func (m *DeviceSecret) Size() (n int) { return n } -func (m *GroupAddDeviceSecret) Size() (n int) { +func (m *GroupAddDeviceChainKey) Size() (n int) { if m == nil { return 0 } @@ -23051,7 +22315,7 @@ func (m *PushMemberTokenUpdate) Size() (n int) { return n } -func (m *PushReceive) Size() (n int) { +func (m *OutOfStoreReceive) Size() (n int) { if m == nil { return 0 } @@ -23063,7 +22327,7 @@ func (m *PushReceive) Size() (n int) { return n } -func (m *PushReceive_Request) Size() (n int) { +func (m *OutOfStoreReceive_Request) Size() (n int) { if m == nil { return 0 } @@ -23079,7 +22343,7 @@ func (m *PushReceive_Request) Size() (n int) { return n } -func (m *PushReceive_Reply) Size() (n int) { +func (m *OutOfStoreReceive_Reply) Size() (n int) { if m == nil { return 0 } @@ -23106,7 +22370,7 @@ func (m *PushReceive_Reply) Size() (n int) { return n } -func (m *PushSend) Size() (n int) { +func (m *OutOfStoreSeal) Size() (n int) { if m == nil { return 0 } @@ -23118,7 +22382,7 @@ func (m *PushSend) Size() (n int) { return n } -func (m *PushSend_Request) Size() (n int) { +func (m *OutOfStoreSeal_Request) Size() (n int) { if m == nil { return 0 } @@ -23132,158 +22396,22 @@ func (m *PushSend_Request) Size() (n int) { if l > 0 { n += 1 + l + sovProtocoltypes(uint64(l)) } - if len(m.GroupMembers) > 0 { - for _, e := range m.GroupMembers { - l = e.Size() - n += 1 + l + sovProtocoltypes(uint64(l)) - } - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n -} - -func (m *PushSend_Reply) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if len(m.GroupMembers) > 0 { - for _, e := range m.GroupMembers { - l = e.Size() - n += 1 + l + sovProtocoltypes(uint64(l)) - } - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n -} - -func (m *PushShareToken) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } -func (m *PushShareToken_Request) Size() (n int) { +func (m *OutOfStoreSeal_Reply) Size() (n int) { if m == nil { return 0 } var l int _ = l - l = len(m.GroupPK) + l = len(m.Encrypted) if l > 0 { n += 1 + l + sovProtocoltypes(uint64(l)) } - if m.Server != nil { - l = m.Server.Size() - n += 1 + l + sovProtocoltypes(uint64(l)) - } - if m.Receiver != nil { - l = m.Receiver.Size() - n += 1 + l + sovProtocoltypes(uint64(l)) - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n -} - -func (m *PushShareToken_Reply) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n -} - -func (m *PushSetDeviceToken) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n -} - -func (m *PushSetDeviceToken_Request) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.Receiver != nil { - l = m.Receiver.Size() - n += 1 + l + sovProtocoltypes(uint64(l)) - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n -} - -func (m *PushSetDeviceToken_Reply) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n -} - -func (m *PushSetServer) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n -} - -func (m *PushSetServer_Request) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.Server != nil { - l = m.Server.Size() - n += 1 + l + sovProtocoltypes(uint64(l)) - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n -} - -func (m *PushSetServer_Reply) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -25538,7 +24666,7 @@ func (m *GroupAddMemberDevice) Unmarshal(dAtA []byte) error { } return nil } -func (m *DeviceSecret) Unmarshal(dAtA []byte) error { +func (m *DeviceChainKey) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -25561,10 +24689,10 @@ func (m *DeviceSecret) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: DeviceSecret: wiretype end group for non-group") + return fmt.Errorf("proto: DeviceChainKey: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: DeviceSecret: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: DeviceChainKey: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: @@ -25642,7 +24770,7 @@ func (m *DeviceSecret) Unmarshal(dAtA []byte) error { } return nil } -func (m *GroupAddDeviceSecret) Unmarshal(dAtA []byte) error { +func (m *GroupAddDeviceChainKey) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -25665,10 +24793,10 @@ func (m *GroupAddDeviceSecret) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: GroupAddDeviceSecret: wiretype end group for non-group") + return fmt.Errorf("proto: GroupAddDeviceChainKey: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: GroupAddDeviceSecret: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: GroupAddDeviceChainKey: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: @@ -41952,7 +41080,7 @@ func (m *PushMemberTokenUpdate) Unmarshal(dAtA []byte) error { } return nil } -func (m *PushReceive) Unmarshal(dAtA []byte) error { +func (m *OutOfStoreReceive) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -41975,10 +41103,10 @@ func (m *PushReceive) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: PushReceive: wiretype end group for non-group") + return fmt.Errorf("proto: OutOfStoreReceive: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: PushReceive: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: OutOfStoreReceive: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { default: @@ -42003,7 +41131,7 @@ func (m *PushReceive) Unmarshal(dAtA []byte) error { } return nil } -func (m *PushReceive_Request) Unmarshal(dAtA []byte) error { +func (m *OutOfStoreReceive_Request) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -42088,7 +41216,7 @@ func (m *PushReceive_Request) Unmarshal(dAtA []byte) error { } return nil } -func (m *PushReceive_Reply) Unmarshal(dAtA []byte) error { +func (m *OutOfStoreReceive_Reply) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -42263,7 +41391,7 @@ func (m *PushReceive_Reply) Unmarshal(dAtA []byte) error { } return nil } -func (m *PushSend) Unmarshal(dAtA []byte) error { +func (m *OutOfStoreSeal) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -42286,10 +41414,10 @@ func (m *PushSend) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: PushSend: wiretype end group for non-group") + return fmt.Errorf("proto: OutOfStoreSeal: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: PushSend: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: OutOfStoreSeal: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { default: @@ -42314,7 +41442,7 @@ func (m *PushSend) Unmarshal(dAtA []byte) error { } return nil } -func (m *PushSend_Request) Unmarshal(dAtA []byte) error { +func (m *OutOfStoreSeal_Request) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -42411,40 +41539,6 @@ func (m *PushSend_Request) Unmarshal(dAtA []byte) error { m.GroupPublicKey = []byte{} } iNdEx = postIndex - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field GroupMembers", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowProtocoltypes - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthProtocoltypes - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthProtocoltypes - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.GroupMembers = append(m.GroupMembers, &MemberWithDevices{}) - if err := m.GroupMembers[len(m.GroupMembers)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipProtocoltypes(dAtA[iNdEx:]) @@ -42467,7 +41561,7 @@ func (m *PushSend_Request) Unmarshal(dAtA []byte) error { } return nil } -func (m *PushSend_Reply) Unmarshal(dAtA []byte) error { +func (m *OutOfStoreSeal_Reply) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -42498,143 +41592,7 @@ func (m *PushSend_Reply) Unmarshal(dAtA []byte) error { switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field GroupMembers", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowProtocoltypes - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthProtocoltypes - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthProtocoltypes - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.GroupMembers = append(m.GroupMembers, &MemberWithDevices{}) - if err := m.GroupMembers[len(m.GroupMembers)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipProtocoltypes(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthProtocoltypes - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *PushShareToken) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowProtocoltypes - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: PushShareToken: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: PushShareToken: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - default: - iNdEx = preIndex - skippy, err := skipProtocoltypes(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthProtocoltypes - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *PushShareToken_Request) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowProtocoltypes - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: Request: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: Request: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field GroupPK", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Encrypted", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { @@ -42661,81 +41619,9 @@ func (m *PushShareToken_Request) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.GroupPK = append(m.GroupPK[:0], dAtA[iNdEx:postIndex]...) - if m.GroupPK == nil { - m.GroupPK = []byte{} - } - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Server", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowProtocoltypes - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthProtocoltypes - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthProtocoltypes - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.Server == nil { - m.Server = &PushServer{} - } - if err := m.Server.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Receiver", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowProtocoltypes - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthProtocoltypes - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthProtocoltypes - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.Receiver == nil { - m.Receiver = &PushServiceReceiver{} - } - if err := m.Receiver.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err + m.Encrypted = append(m.Encrypted[:0], dAtA[iNdEx:postIndex]...) + if m.Encrypted == nil { + m.Encrypted = []byte{} } iNdEx = postIndex default: @@ -42760,435 +41646,6 @@ func (m *PushShareToken_Request) Unmarshal(dAtA []byte) error { } return nil } -func (m *PushShareToken_Reply) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowProtocoltypes - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: Reply: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: Reply: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - default: - iNdEx = preIndex - skippy, err := skipProtocoltypes(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthProtocoltypes - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *PushSetDeviceToken) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowProtocoltypes - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: PushSetDeviceToken: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: PushSetDeviceToken: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - default: - iNdEx = preIndex - skippy, err := skipProtocoltypes(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthProtocoltypes - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *PushSetDeviceToken_Request) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowProtocoltypes - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: Request: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: Request: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Receiver", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowProtocoltypes - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthProtocoltypes - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthProtocoltypes - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.Receiver == nil { - m.Receiver = &PushServiceReceiver{} - } - if err := m.Receiver.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipProtocoltypes(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthProtocoltypes - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *PushSetDeviceToken_Reply) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowProtocoltypes - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: Reply: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: Reply: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - default: - iNdEx = preIndex - skippy, err := skipProtocoltypes(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthProtocoltypes - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *PushSetServer) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowProtocoltypes - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: PushSetServer: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: PushSetServer: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - default: - iNdEx = preIndex - skippy, err := skipProtocoltypes(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthProtocoltypes - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *PushSetServer_Request) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowProtocoltypes - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: Request: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: Request: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Server", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowProtocoltypes - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthProtocoltypes - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthProtocoltypes - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.Server == nil { - m.Server = &PushServer{} - } - if err := m.Server.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipProtocoltypes(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthProtocoltypes - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *PushSetServer_Reply) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowProtocoltypes - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: Reply: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: Reply: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - default: - iNdEx = preIndex - skippy, err := skipProtocoltypes(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthProtocoltypes - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} func (m *FirstLastCounters) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 diff --git a/pkg/protocoltypes/protocoltypes.pb.gw.go b/pkg/protocoltypes/protocoltypes.pb.gw.go index cfdbafd9..501f915d 100644 --- a/pkg/protocoltypes/protocoltypes.pb.gw.go +++ b/pkg/protocoltypes/protocoltypes.pb.gw.go @@ -1287,8 +1287,8 @@ func local_request_ProtocolService_PeerList_0(ctx context.Context, marshaler run } -func request_ProtocolService_PushReceive_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq PushReceive_Request +func request_ProtocolService_OutOfStoreReceive_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq OutOfStoreReceive_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) @@ -1299,13 +1299,13 @@ func request_ProtocolService_PushReceive_0(ctx context.Context, marshaler runtim return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } - msg, err := client.PushReceive(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + msg, err := client.OutOfStoreReceive(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } -func local_request_ProtocolService_PushReceive_0(ctx context.Context, marshaler runtime.Marshaler, server ProtocolServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq PushReceive_Request +func local_request_ProtocolService_OutOfStoreReceive_0(ctx context.Context, marshaler runtime.Marshaler, server ProtocolServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq OutOfStoreReceive_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) @@ -1316,13 +1316,13 @@ func local_request_ProtocolService_PushReceive_0(ctx context.Context, marshaler return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } - msg, err := server.PushReceive(ctx, &protoReq) + msg, err := server.OutOfStoreReceive(ctx, &protoReq) return msg, metadata, err } -func request_ProtocolService_PushSend_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq PushSend_Request +func request_ProtocolService_OutOfStoreSeal_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq OutOfStoreSeal_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) @@ -1333,13 +1333,13 @@ func request_ProtocolService_PushSend_0(ctx context.Context, marshaler runtime.M return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } - msg, err := client.PushSend(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + msg, err := client.OutOfStoreSeal(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } -func local_request_ProtocolService_PushSend_0(ctx context.Context, marshaler runtime.Marshaler, server ProtocolServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq PushSend_Request +func local_request_ProtocolService_OutOfStoreSeal_0(ctx context.Context, marshaler runtime.Marshaler, server ProtocolServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq OutOfStoreSeal_Request var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) @@ -1350,109 +1350,7 @@ func local_request_ProtocolService_PushSend_0(ctx context.Context, marshaler run return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } - msg, err := server.PushSend(ctx, &protoReq) - return msg, metadata, err - -} - -func request_ProtocolService_PushShareToken_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq PushShareToken_Request - var metadata runtime.ServerMetadata - - newReader, berr := utilities.IOReaderFactory(req.Body) - if berr != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) - } - if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - - msg, err := client.PushShareToken(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) - return msg, metadata, err - -} - -func local_request_ProtocolService_PushShareToken_0(ctx context.Context, marshaler runtime.Marshaler, server ProtocolServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq PushShareToken_Request - var metadata runtime.ServerMetadata - - newReader, berr := utilities.IOReaderFactory(req.Body) - if berr != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) - } - if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - - msg, err := server.PushShareToken(ctx, &protoReq) - return msg, metadata, err - -} - -func request_ProtocolService_PushSetDeviceToken_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq PushSetDeviceToken_Request - var metadata runtime.ServerMetadata - - newReader, berr := utilities.IOReaderFactory(req.Body) - if berr != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) - } - if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - - msg, err := client.PushSetDeviceToken(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) - return msg, metadata, err - -} - -func local_request_ProtocolService_PushSetDeviceToken_0(ctx context.Context, marshaler runtime.Marshaler, server ProtocolServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq PushSetDeviceToken_Request - var metadata runtime.ServerMetadata - - newReader, berr := utilities.IOReaderFactory(req.Body) - if berr != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) - } - if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - - msg, err := server.PushSetDeviceToken(ctx, &protoReq) - return msg, metadata, err - -} - -func request_ProtocolService_PushSetServer_0(ctx context.Context, marshaler runtime.Marshaler, client ProtocolServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq PushSetServer_Request - var metadata runtime.ServerMetadata - - newReader, berr := utilities.IOReaderFactory(req.Body) - if berr != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) - } - if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - - msg, err := client.PushSetServer(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) - return msg, metadata, err - -} - -func local_request_ProtocolService_PushSetServer_0(ctx context.Context, marshaler runtime.Marshaler, server ProtocolServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq PushSetServer_Request - var metadata runtime.ServerMetadata - - newReader, berr := utilities.IOReaderFactory(req.Body) - if berr != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) - } - if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - - msg, err := server.PushSetServer(ctx, &protoReq) + msg, err := server.OutOfStoreSeal(ctx, &protoReq) return msg, metadata, err } @@ -2266,7 +2164,7 @@ func RegisterProtocolServiceHandlerServer(ctx context.Context, mux *runtime.Serv }) - mux.Handle("POST", pattern_ProtocolService_PushReceive_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("POST", pattern_ProtocolService_OutOfStoreReceive_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() var stream runtime.ServerTransportStream @@ -2277,7 +2175,7 @@ func RegisterProtocolServiceHandlerServer(ctx context.Context, mux *runtime.Serv runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := local_request_ProtocolService_PushReceive_0(rctx, inboundMarshaler, server, req, pathParams) + resp, md, err := local_request_ProtocolService_OutOfStoreReceive_0(rctx, inboundMarshaler, server, req, pathParams) md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { @@ -2285,11 +2183,11 @@ func RegisterProtocolServiceHandlerServer(ctx context.Context, mux *runtime.Serv return } - forward_ProtocolService_PushReceive_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_ProtocolService_OutOfStoreReceive_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) - mux.Handle("POST", pattern_ProtocolService_PushSend_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("POST", pattern_ProtocolService_OutOfStoreSeal_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() var stream runtime.ServerTransportStream @@ -2300,7 +2198,7 @@ func RegisterProtocolServiceHandlerServer(ctx context.Context, mux *runtime.Serv runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := local_request_ProtocolService_PushSend_0(rctx, inboundMarshaler, server, req, pathParams) + resp, md, err := local_request_ProtocolService_OutOfStoreSeal_0(rctx, inboundMarshaler, server, req, pathParams) md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { @@ -2308,76 +2206,7 @@ func RegisterProtocolServiceHandlerServer(ctx context.Context, mux *runtime.Serv return } - forward_ProtocolService_PushSend_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - - }) - - mux.Handle("POST", pattern_ProtocolService_PushShareToken_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - var stream runtime.ServerTransportStream - ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := local_request_ProtocolService_PushShareToken_0(rctx, inboundMarshaler, server, req, pathParams) - md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_ProtocolService_PushShareToken_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - - }) - - mux.Handle("POST", pattern_ProtocolService_PushSetDeviceToken_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - var stream runtime.ServerTransportStream - ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := local_request_ProtocolService_PushSetDeviceToken_0(rctx, inboundMarshaler, server, req, pathParams) - md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_ProtocolService_PushSetDeviceToken_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - - }) - - mux.Handle("POST", pattern_ProtocolService_PushSetServer_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - var stream runtime.ServerTransportStream - ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := local_request_ProtocolService_PushSetServer_0(rctx, inboundMarshaler, server, req, pathParams) - md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_ProtocolService_PushSetServer_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_ProtocolService_OutOfStoreSeal_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) @@ -3225,47 +3054,7 @@ func RegisterProtocolServiceHandlerClient(ctx context.Context, mux *runtime.Serv }) - mux.Handle("POST", pattern_ProtocolService_PushReceive_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := request_ProtocolService_PushReceive_0(rctx, inboundMarshaler, client, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_ProtocolService_PushReceive_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - - }) - - mux.Handle("POST", pattern_ProtocolService_PushSend_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := request_ProtocolService_PushSend_0(rctx, inboundMarshaler, client, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_ProtocolService_PushSend_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - - }) - - mux.Handle("POST", pattern_ProtocolService_PushShareToken_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("POST", pattern_ProtocolService_OutOfStoreReceive_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) @@ -3274,18 +3063,18 @@ func RegisterProtocolServiceHandlerClient(ctx context.Context, mux *runtime.Serv runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := request_ProtocolService_PushShareToken_0(rctx, inboundMarshaler, client, req, pathParams) + resp, md, err := request_ProtocolService_OutOfStoreReceive_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - forward_ProtocolService_PushShareToken_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_ProtocolService_OutOfStoreReceive_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) - mux.Handle("POST", pattern_ProtocolService_PushSetDeviceToken_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("POST", pattern_ProtocolService_OutOfStoreSeal_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) @@ -3294,34 +3083,14 @@ func RegisterProtocolServiceHandlerClient(ctx context.Context, mux *runtime.Serv runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := request_ProtocolService_PushSetDeviceToken_0(rctx, inboundMarshaler, client, req, pathParams) + resp, md, err := request_ProtocolService_OutOfStoreSeal_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - forward_ProtocolService_PushSetDeviceToken_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - - }) - - mux.Handle("POST", pattern_ProtocolService_PushSetServer_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := request_ProtocolService_PushSetServer_0(rctx, inboundMarshaler, client, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_ProtocolService_PushSetServer_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_ProtocolService_OutOfStoreSeal_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) @@ -3427,15 +3196,9 @@ var ( pattern_ProtocolService_PeerList_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"weshnet.protocol.v1.ProtocolService", "PeerList"}, "", runtime.AssumeColonVerbOpt(true))) - pattern_ProtocolService_PushReceive_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"weshnet.protocol.v1.ProtocolService", "PushReceive"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_ProtocolService_OutOfStoreReceive_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"weshnet.protocol.v1.ProtocolService", "OutOfStoreReceive"}, "", runtime.AssumeColonVerbOpt(true))) - pattern_ProtocolService_PushSend_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"weshnet.protocol.v1.ProtocolService", "PushSend"}, "", runtime.AssumeColonVerbOpt(true))) - - pattern_ProtocolService_PushShareToken_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"weshnet.protocol.v1.ProtocolService", "PushShareToken"}, "", runtime.AssumeColonVerbOpt(true))) - - pattern_ProtocolService_PushSetDeviceToken_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"weshnet.protocol.v1.ProtocolService", "PushSetDeviceToken"}, "", runtime.AssumeColonVerbOpt(true))) - - pattern_ProtocolService_PushSetServer_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"weshnet.protocol.v1.ProtocolService", "PushSetServer"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_ProtocolService_OutOfStoreSeal_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"weshnet.protocol.v1.ProtocolService", "OutOfStoreSeal"}, "", runtime.AssumeColonVerbOpt(true))) pattern_ProtocolService_RefreshContactRequest_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"weshnet.protocol.v1.ProtocolService", "RefreshContactRequest"}, "", runtime.AssumeColonVerbOpt(true))) ) @@ -3519,15 +3282,9 @@ var ( forward_ProtocolService_PeerList_0 = runtime.ForwardResponseMessage - forward_ProtocolService_PushReceive_0 = runtime.ForwardResponseMessage - - forward_ProtocolService_PushSend_0 = runtime.ForwardResponseMessage - - forward_ProtocolService_PushShareToken_0 = runtime.ForwardResponseMessage - - forward_ProtocolService_PushSetDeviceToken_0 = runtime.ForwardResponseMessage + forward_ProtocolService_OutOfStoreReceive_0 = runtime.ForwardResponseMessage - forward_ProtocolService_PushSetServer_0 = runtime.ForwardResponseMessage + forward_ProtocolService_OutOfStoreSeal_0 = runtime.ForwardResponseMessage forward_ProtocolService_RefreshContactRequest_0 = runtime.ForwardResponseMessage ) diff --git a/pkg/protocoltypes/protocoltypes_grpc.pb.go b/pkg/protocoltypes/protocoltypes_grpc.pb.go index 003db871..e60c9048 100644 --- a/pkg/protocoltypes/protocoltypes_grpc.pb.go +++ b/pkg/protocoltypes/protocoltypes_grpc.pb.go @@ -91,16 +91,10 @@ type ProtocolServiceClient interface { ReplicationServiceRegisterGroup(ctx context.Context, in *ReplicationServiceRegisterGroup_Request, opts ...grpc.CallOption) (*ReplicationServiceRegisterGroup_Reply, error) // PeerList returns a list of P2P peers PeerList(ctx context.Context, in *PeerList_Request, opts ...grpc.CallOption) (*PeerList_Reply, error) - // PushReceive handles a push payload, decrypts it if possible - PushReceive(ctx context.Context, in *PushReceive_Request, opts ...grpc.CallOption) (*PushReceive_Reply, error) - // PushSend sends a push payload to a specified list of group members - PushSend(ctx context.Context, in *PushSend_Request, opts ...grpc.CallOption) (*PushSend_Reply, error) - // PushShareToken sends push tokens of own devices to a group - PushShareToken(ctx context.Context, in *PushShareToken_Request, opts ...grpc.CallOption) (*PushShareToken_Reply, error) - // PushSetDeviceToken registers a push token for the current device - PushSetDeviceToken(ctx context.Context, in *PushSetDeviceToken_Request, opts ...grpc.CallOption) (*PushSetDeviceToken_Reply, error) - // PushSetServer registers a push server for the current device - PushSetServer(ctx context.Context, in *PushSetServer_Request, opts ...grpc.CallOption) (*PushSetServer_Reply, error) + // OutOfStoreReceive parses a payload received outside a synchronized store + OutOfStoreReceive(ctx context.Context, in *OutOfStoreReceive_Request, opts ...grpc.CallOption) (*OutOfStoreReceive_Reply, error) + // OutOfStoreSeal creates a payload of a message present in store to be sent outside a synchronized store + OutOfStoreSeal(ctx context.Context, in *OutOfStoreSeal_Request, opts ...grpc.CallOption) (*OutOfStoreSeal_Reply, error) // RefreshContactRequest try to refresh the contact request for the given contact RefreshContactRequest(ctx context.Context, in *RefreshContactRequest_Request, opts ...grpc.CallOption) (*RefreshContactRequest_Reply, error) } @@ -648,45 +642,18 @@ func (c *protocolServiceClient) PeerList(ctx context.Context, in *PeerList_Reque return out, nil } -func (c *protocolServiceClient) PushReceive(ctx context.Context, in *PushReceive_Request, opts ...grpc.CallOption) (*PushReceive_Reply, error) { - out := new(PushReceive_Reply) - err := c.cc.Invoke(ctx, "/weshnet.protocol.v1.ProtocolService/PushReceive", in, out, opts...) +func (c *protocolServiceClient) OutOfStoreReceive(ctx context.Context, in *OutOfStoreReceive_Request, opts ...grpc.CallOption) (*OutOfStoreReceive_Reply, error) { + out := new(OutOfStoreReceive_Reply) + err := c.cc.Invoke(ctx, "/weshnet.protocol.v1.ProtocolService/OutOfStoreReceive", in, out, opts...) if err != nil { return nil, err } return out, nil } -func (c *protocolServiceClient) PushSend(ctx context.Context, in *PushSend_Request, opts ...grpc.CallOption) (*PushSend_Reply, error) { - out := new(PushSend_Reply) - err := c.cc.Invoke(ctx, "/weshnet.protocol.v1.ProtocolService/PushSend", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *protocolServiceClient) PushShareToken(ctx context.Context, in *PushShareToken_Request, opts ...grpc.CallOption) (*PushShareToken_Reply, error) { - out := new(PushShareToken_Reply) - err := c.cc.Invoke(ctx, "/weshnet.protocol.v1.ProtocolService/PushShareToken", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *protocolServiceClient) PushSetDeviceToken(ctx context.Context, in *PushSetDeviceToken_Request, opts ...grpc.CallOption) (*PushSetDeviceToken_Reply, error) { - out := new(PushSetDeviceToken_Reply) - err := c.cc.Invoke(ctx, "/weshnet.protocol.v1.ProtocolService/PushSetDeviceToken", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *protocolServiceClient) PushSetServer(ctx context.Context, in *PushSetServer_Request, opts ...grpc.CallOption) (*PushSetServer_Reply, error) { - out := new(PushSetServer_Reply) - err := c.cc.Invoke(ctx, "/weshnet.protocol.v1.ProtocolService/PushSetServer", in, out, opts...) +func (c *protocolServiceClient) OutOfStoreSeal(ctx context.Context, in *OutOfStoreSeal_Request, opts ...grpc.CallOption) (*OutOfStoreSeal_Reply, error) { + out := new(OutOfStoreSeal_Reply) + err := c.cc.Invoke(ctx, "/weshnet.protocol.v1.ProtocolService/OutOfStoreSeal", in, out, opts...) if err != nil { return nil, err } @@ -779,16 +746,10 @@ type ProtocolServiceServer interface { ReplicationServiceRegisterGroup(context.Context, *ReplicationServiceRegisterGroup_Request) (*ReplicationServiceRegisterGroup_Reply, error) // PeerList returns a list of P2P peers PeerList(context.Context, *PeerList_Request) (*PeerList_Reply, error) - // PushReceive handles a push payload, decrypts it if possible - PushReceive(context.Context, *PushReceive_Request) (*PushReceive_Reply, error) - // PushSend sends a push payload to a specified list of group members - PushSend(context.Context, *PushSend_Request) (*PushSend_Reply, error) - // PushShareToken sends push tokens of own devices to a group - PushShareToken(context.Context, *PushShareToken_Request) (*PushShareToken_Reply, error) - // PushSetDeviceToken registers a push token for the current device - PushSetDeviceToken(context.Context, *PushSetDeviceToken_Request) (*PushSetDeviceToken_Reply, error) - // PushSetServer registers a push server for the current device - PushSetServer(context.Context, *PushSetServer_Request) (*PushSetServer_Reply, error) + // OutOfStoreReceive parses a payload received outside a synchronized store + OutOfStoreReceive(context.Context, *OutOfStoreReceive_Request) (*OutOfStoreReceive_Reply, error) + // OutOfStoreSeal creates a payload of a message present in store to be sent outside a synchronized store + OutOfStoreSeal(context.Context, *OutOfStoreSeal_Request) (*OutOfStoreSeal_Reply, error) // RefreshContactRequest try to refresh the contact request for the given contact RefreshContactRequest(context.Context, *RefreshContactRequest_Request) (*RefreshContactRequest_Reply, error) mustEmbedUnimplementedProtocolServiceServer() @@ -915,20 +876,11 @@ func (UnimplementedProtocolServiceServer) ReplicationServiceRegisterGroup(contex func (UnimplementedProtocolServiceServer) PeerList(context.Context, *PeerList_Request) (*PeerList_Reply, error) { return nil, status.Errorf(codes.Unimplemented, "method PeerList not implemented") } -func (UnimplementedProtocolServiceServer) PushReceive(context.Context, *PushReceive_Request) (*PushReceive_Reply, error) { - return nil, status.Errorf(codes.Unimplemented, "method PushReceive not implemented") -} -func (UnimplementedProtocolServiceServer) PushSend(context.Context, *PushSend_Request) (*PushSend_Reply, error) { - return nil, status.Errorf(codes.Unimplemented, "method PushSend not implemented") -} -func (UnimplementedProtocolServiceServer) PushShareToken(context.Context, *PushShareToken_Request) (*PushShareToken_Reply, error) { - return nil, status.Errorf(codes.Unimplemented, "method PushShareToken not implemented") -} -func (UnimplementedProtocolServiceServer) PushSetDeviceToken(context.Context, *PushSetDeviceToken_Request) (*PushSetDeviceToken_Reply, error) { - return nil, status.Errorf(codes.Unimplemented, "method PushSetDeviceToken not implemented") +func (UnimplementedProtocolServiceServer) OutOfStoreReceive(context.Context, *OutOfStoreReceive_Request) (*OutOfStoreReceive_Reply, error) { + return nil, status.Errorf(codes.Unimplemented, "method OutOfStoreReceive not implemented") } -func (UnimplementedProtocolServiceServer) PushSetServer(context.Context, *PushSetServer_Request) (*PushSetServer_Reply, error) { - return nil, status.Errorf(codes.Unimplemented, "method PushSetServer not implemented") +func (UnimplementedProtocolServiceServer) OutOfStoreSeal(context.Context, *OutOfStoreSeal_Request) (*OutOfStoreSeal_Reply, error) { + return nil, status.Errorf(codes.Unimplemented, "method OutOfStoreSeal not implemented") } func (UnimplementedProtocolServiceServer) RefreshContactRequest(context.Context, *RefreshContactRequest_Request) (*RefreshContactRequest_Reply, error) { return nil, status.Errorf(codes.Unimplemented, "method RefreshContactRequest not implemented") @@ -1672,92 +1624,38 @@ func _ProtocolService_PeerList_Handler(srv interface{}, ctx context.Context, dec return interceptor(ctx, in, info, handler) } -func _ProtocolService_PushReceive_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(PushReceive_Request) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ProtocolServiceServer).PushReceive(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/weshnet.protocol.v1.ProtocolService/PushReceive", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ProtocolServiceServer).PushReceive(ctx, req.(*PushReceive_Request)) - } - return interceptor(ctx, in, info, handler) -} - -func _ProtocolService_PushSend_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(PushSend_Request) +func _ProtocolService_OutOfStoreReceive_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(OutOfStoreReceive_Request) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(ProtocolServiceServer).PushSend(ctx, in) + return srv.(ProtocolServiceServer).OutOfStoreReceive(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/weshnet.protocol.v1.ProtocolService/PushSend", + FullMethod: "/weshnet.protocol.v1.ProtocolService/OutOfStoreReceive", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ProtocolServiceServer).PushSend(ctx, req.(*PushSend_Request)) + return srv.(ProtocolServiceServer).OutOfStoreReceive(ctx, req.(*OutOfStoreReceive_Request)) } return interceptor(ctx, in, info, handler) } -func _ProtocolService_PushShareToken_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(PushShareToken_Request) +func _ProtocolService_OutOfStoreSeal_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(OutOfStoreSeal_Request) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(ProtocolServiceServer).PushShareToken(ctx, in) + return srv.(ProtocolServiceServer).OutOfStoreSeal(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/weshnet.protocol.v1.ProtocolService/PushShareToken", + FullMethod: "/weshnet.protocol.v1.ProtocolService/OutOfStoreSeal", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ProtocolServiceServer).PushShareToken(ctx, req.(*PushShareToken_Request)) - } - return interceptor(ctx, in, info, handler) -} - -func _ProtocolService_PushSetDeviceToken_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(PushSetDeviceToken_Request) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ProtocolServiceServer).PushSetDeviceToken(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/weshnet.protocol.v1.ProtocolService/PushSetDeviceToken", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ProtocolServiceServer).PushSetDeviceToken(ctx, req.(*PushSetDeviceToken_Request)) - } - return interceptor(ctx, in, info, handler) -} - -func _ProtocolService_PushSetServer_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(PushSetServer_Request) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ProtocolServiceServer).PushSetServer(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/weshnet.protocol.v1.ProtocolService/PushSetServer", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ProtocolServiceServer).PushSetServer(ctx, req.(*PushSetServer_Request)) + return srv.(ProtocolServiceServer).OutOfStoreSeal(ctx, req.(*OutOfStoreSeal_Request)) } return interceptor(ctx, in, info, handler) } @@ -1912,24 +1810,12 @@ var ProtocolService_ServiceDesc = grpc.ServiceDesc{ Handler: _ProtocolService_PeerList_Handler, }, { - MethodName: "PushReceive", - Handler: _ProtocolService_PushReceive_Handler, - }, - { - MethodName: "PushSend", - Handler: _ProtocolService_PushSend_Handler, - }, - { - MethodName: "PushShareToken", - Handler: _ProtocolService_PushShareToken_Handler, - }, - { - MethodName: "PushSetDeviceToken", - Handler: _ProtocolService_PushSetDeviceToken_Handler, + MethodName: "OutOfStoreReceive", + Handler: _ProtocolService_OutOfStoreReceive_Handler, }, { - MethodName: "PushSetServer", - Handler: _ProtocolService_PushSetServer_Handler, + MethodName: "OutOfStoreSeal", + Handler: _ProtocolService_OutOfStoreSeal_Handler, }, { MethodName: "RefreshContactRequest", diff --git a/pkg/pushtypes/pushtypes.pb.go b/pkg/pushtypes/pushtypes.pb.go index 7f8a5a60..d096d977 100644 --- a/pkg/pushtypes/pushtypes.pb.go +++ b/pkg/pushtypes/pushtypes.pb.go @@ -529,7 +529,7 @@ func (m *OutOfStoreMessageEnvelope) GetGroupReference() []byte { return nil } -type PushExposedData struct { +type OutOfStoreExposedData struct { Nonce []byte `protobuf:"bytes,1,opt,name=nonce,proto3" json:"nonce,omitempty"` Box []byte `protobuf:"bytes,2,opt,name=box,proto3" json:"box,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` @@ -537,18 +537,18 @@ type PushExposedData struct { XXX_sizecache int32 `json:"-"` } -func (m *PushExposedData) Reset() { *m = PushExposedData{} } -func (m *PushExposedData) String() string { return proto.CompactTextString(m) } -func (*PushExposedData) ProtoMessage() {} -func (*PushExposedData) Descriptor() ([]byte, []int) { +func (m *OutOfStoreExposedData) Reset() { *m = OutOfStoreExposedData{} } +func (m *OutOfStoreExposedData) String() string { return proto.CompactTextString(m) } +func (*OutOfStoreExposedData) ProtoMessage() {} +func (*OutOfStoreExposedData) Descriptor() ([]byte, []int) { return fileDescriptor_6b0fb36a3e3f285e, []int{4} } -func (m *PushExposedData) XXX_Unmarshal(b []byte) error { +func (m *OutOfStoreExposedData) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } -func (m *PushExposedData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { +func (m *OutOfStoreExposedData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { - return xxx_messageInfo_PushExposedData.Marshal(b, m, deterministic) + return xxx_messageInfo_OutOfStoreExposedData.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) @@ -558,26 +558,26 @@ func (m *PushExposedData) XXX_Marshal(b []byte, deterministic bool) ([]byte, err return b[:n], nil } } -func (m *PushExposedData) XXX_Merge(src proto.Message) { - xxx_messageInfo_PushExposedData.Merge(m, src) +func (m *OutOfStoreExposedData) XXX_Merge(src proto.Message) { + xxx_messageInfo_OutOfStoreExposedData.Merge(m, src) } -func (m *PushExposedData) XXX_Size() int { +func (m *OutOfStoreExposedData) XXX_Size() int { return m.Size() } -func (m *PushExposedData) XXX_DiscardUnknown() { - xxx_messageInfo_PushExposedData.DiscardUnknown(m) +func (m *OutOfStoreExposedData) XXX_DiscardUnknown() { + xxx_messageInfo_OutOfStoreExposedData.DiscardUnknown(m) } -var xxx_messageInfo_PushExposedData proto.InternalMessageInfo +var xxx_messageInfo_OutOfStoreExposedData proto.InternalMessageInfo -func (m *PushExposedData) GetNonce() []byte { +func (m *OutOfStoreExposedData) GetNonce() []byte { if m != nil { return m.Nonce } return nil } -func (m *PushExposedData) GetBox() []byte { +func (m *OutOfStoreExposedData) GetBox() []byte { if m != nil { return m.Box } @@ -897,7 +897,7 @@ func init() { proto.RegisterType((*PushServiceSend_Request)(nil), "weshnet.push.v1.PushServiceSend.Request") proto.RegisterType((*PushServiceSend_Reply)(nil), "weshnet.push.v1.PushServiceSend.Reply") proto.RegisterType((*OutOfStoreMessageEnvelope)(nil), "weshnet.push.v1.OutOfStoreMessageEnvelope") - proto.RegisterType((*PushExposedData)(nil), "weshnet.push.v1.PushExposedData") + proto.RegisterType((*OutOfStoreExposedData)(nil), "weshnet.push.v1.OutOfStoreExposedData") proto.RegisterType((*PushServiceOpaqueReceiver)(nil), "weshnet.push.v1.PushServiceOpaqueReceiver") proto.RegisterType((*DecryptedPush)(nil), "weshnet.push.v1.DecryptedPush") proto.RegisterType((*FormatedPush)(nil), "weshnet.push.v1.FormatedPush") @@ -906,83 +906,83 @@ func init() { func init() { proto.RegisterFile("pushtypes/pushtypes.proto", fileDescriptor_6b0fb36a3e3f285e) } var fileDescriptor_6b0fb36a3e3f285e = []byte{ - // 1211 bytes of a gzipped FileDescriptorProto + // 1213 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x56, 0xcd, 0x6e, 0xdb, 0xc6, - 0x16, 0x0e, 0xf5, 0x67, 0xea, 0xc8, 0x8e, 0xe8, 0xb1, 0x9c, 0xc8, 0x0a, 0x62, 0x3b, 0xcc, 0xcd, - 0x8d, 0xe3, 0x9b, 0x2b, 0xe3, 0x3a, 0xc0, 0x2d, 0xda, 0x6e, 0x6a, 0x47, 0x49, 0xe3, 0x26, 0xfe, - 0x29, 0xe3, 0xa0, 0x45, 0x81, 0x42, 0xa0, 0x34, 0xc7, 0x32, 0x63, 0x6a, 0x66, 0xc2, 0x1f, 0x3b, - 0xec, 0xaa, 0xeb, 0x3e, 0x42, 0x5f, 0xa0, 0x28, 0xba, 0xe8, 0x6b, 0x74, 0x59, 0xa0, 0xeb, 0x1a, - 0x85, 0x1e, 0xa3, 0x8b, 0xa2, 0x98, 0x19, 0x92, 0x92, 0xe2, 0xc4, 0x08, 0xba, 0xd2, 0x9c, 0xef, - 0x7c, 0xe7, 0x47, 0x73, 0xbe, 0x03, 0x0e, 0x2c, 0x89, 0x38, 0x3c, 0x8e, 0x12, 0x81, 0xe1, 0x46, - 0x7e, 0x6a, 0x8b, 0x80, 0x47, 0x9c, 0xd4, 0xcf, 0x30, 0x3c, 0x66, 0x18, 0xb5, 0xa5, 0xa3, 0x7d, - 0xfa, 0xbf, 0x56, 0x63, 0xc0, 0x07, 0x5c, 0xf9, 0x36, 0xe4, 0x49, 0xd3, 0xec, 0x9f, 0x0d, 0x58, - 0x3c, 0x88, 0xc3, 0xe3, 0xe7, 0x18, 0x9c, 0x7a, 0x7d, 0x94, 0x3f, 0x18, 0xec, 0xb0, 0x23, 0xde, - 0xaa, 0xc2, 0x8c, 0x83, 0xaf, 0x62, 0x0c, 0xa3, 0xd6, 0x77, 0x06, 0x94, 0x1d, 0x14, 0x7e, 0x42, - 0x6e, 0x02, 0x88, 0xb8, 0xe7, 0x7b, 0xfd, 0xee, 0x09, 0x26, 0x4d, 0x63, 0xd5, 0x58, 0x9b, 0x75, - 0xaa, 0x1a, 0x79, 0x8a, 0x09, 0xe9, 0xc1, 0x62, 0x18, 0x0b, 0xc1, 0x83, 0x08, 0x69, 0x37, 0xe2, - 0x27, 0xc8, 0xba, 0xaa, 0xa7, 0x66, 0x61, 0xb5, 0xb8, 0x56, 0xdb, 0x6c, 0xb7, 0xdf, 0x68, 0xaa, - 0x3d, 0x59, 0x3a, 0x0b, 0x3c, 0x94, 0x71, 0x87, 0x89, 0x40, 0x67, 0x21, 0xbc, 0x80, 0x85, 0xf6, - 0xf7, 0x06, 0xdc, 0xbc, 0x34, 0x8c, 0x3c, 0x80, 0x39, 0x57, 0x88, 0x6e, 0x2f, 0x66, 0xd4, 0xc7, - 0xae, 0x47, 0x55, 0x9f, 0xd5, 0xed, 0xfa, 0xe8, 0x7c, 0xa5, 0xb6, 0x25, 0xc4, 0xb6, 0xc2, 0x77, - 0x3a, 0x4e, 0xcd, 0xcd, 0x0d, 0x4a, 0x3a, 0x00, 0xe3, 0x86, 0x9b, 0x85, 0x55, 0x63, 0xed, 0xea, - 0xe6, 0x9d, 0xcb, 0xfa, 0x1d, 0xb7, 0x59, 0x8d, 0xb2, 0xa3, 0xfd, 0xa7, 0x01, 0xf5, 0xa9, 0xeb, - 0x64, 0xb4, 0xf5, 0xbb, 0x91, 0xdf, 0x24, 0x79, 0x0c, 0x26, 0xb2, 0x53, 0xf4, 0xb9, 0x40, 0xd5, - 0x55, 0x6d, 0x73, 0xfd, 0x42, 0x8d, 0xfd, 0x38, 0xda, 0x3f, 0x7a, 0x1e, 0xf1, 0x00, 0x77, 0x31, - 0x0c, 0xdd, 0x01, 0x3e, 0x4a, 0x23, 0x9c, 0x3c, 0x96, 0x7c, 0x02, 0xa6, 0x08, 0x3c, 0x1e, 0x78, - 0x51, 0x92, 0xf6, 0xfa, 0xaf, 0xcb, 0x7a, 0x3d, 0x48, 0xb9, 0x4e, 0x1e, 0x45, 0x9e, 0x40, 0x35, - 0xc0, 0x3e, 0x7a, 0xa7, 0x18, 0x84, 0xcd, 0xa2, 0x1a, 0xcf, 0xfa, 0x65, 0x29, 0xf6, 0x85, 0xfb, - 0x2a, 0x46, 0x27, 0x0d, 0x71, 0xc6, 0xc1, 0xad, 0x99, 0x54, 0x1c, 0xf6, 0xb7, 0x06, 0x2c, 0xbd, - 0xb3, 0x79, 0xd2, 0x80, 0x32, 0xe3, 0xac, 0x8f, 0xa9, 0x6a, 0xb4, 0x41, 0x2c, 0x28, 0xf6, 0xf8, - 0x6b, 0xf5, 0x1f, 0x66, 0x1d, 0x79, 0x24, 0x1f, 0x43, 0x7d, 0x10, 0xf0, 0x58, 0x74, 0x03, 0x3c, - 0xc2, 0x00, 0x65, 0x44, 0x49, 0x7a, 0xb7, 0xc9, 0xe8, 0x7c, 0xe5, 0xea, 0xa7, 0xd2, 0xe5, 0x64, - 0x1e, 0xe7, 0xea, 0x60, 0xca, 0xb6, 0x3f, 0xd4, 0xd7, 0xff, 0xe8, 0xb5, 0xe0, 0x21, 0xd2, 0x8e, - 0x1b, 0xb9, 0xef, 0x5b, 0xd7, 0x76, 0x61, 0xe9, 0x9d, 0x7f, 0x97, 0xdc, 0x82, 0x59, 0xae, 0x10, - 0xad, 0xea, 0x34, 0x57, 0x4d, 0x63, 0x4a, 0x09, 0x92, 0x12, 0xea, 0xd8, 0xae, 0x4b, 0x69, 0xa0, - 0x52, 0x57, 0x9d, 0x5a, 0x8a, 0x6d, 0x51, 0x1a, 0xd8, 0x3f, 0x54, 0x60, 0xae, 0x83, 0xfd, 0x20, - 0x11, 0x11, 0x52, 0x59, 0x8c, 0xdc, 0x07, 0x70, 0xfb, 0x7d, 0x1e, 0xb3, 0x68, 0xac, 0xd3, 0xb9, - 0xd1, 0xf9, 0x4a, 0x75, 0x4b, 0xa3, 0x3b, 0x1d, 0xa7, 0x9a, 0x12, 0x76, 0xa8, 0x2c, 0x91, 0xb1, - 0x99, 0x3b, 0xc4, 0xac, 0x44, 0x8a, 0xed, 0xb9, 0x43, 0x24, 0xff, 0x87, 0xeb, 0x7d, 0xce, 0xe4, - 0x5c, 0xdc, 0xc8, 0xe3, 0xac, 0x3b, 0xb1, 0xad, 0x45, 0xc5, 0x5e, 0x9c, 0x74, 0x1f, 0xe4, 0x9b, - 0xfb, 0x11, 0x2c, 0x4d, 0xc5, 0x51, 0x2f, 0x14, 0xbe, 0x9b, 0xe8, 0x3a, 0x25, 0x15, 0x39, 0x95, - 0xb8, 0xa3, 0xfd, 0xaa, 0xe6, 0x3a, 0xcc, 0x0f, 0x71, 0xd8, 0xc3, 0x60, 0xb2, 0x5a, 0x59, 0xc5, - 0xd4, 0xb5, 0x63, 0x5c, 0xa7, 0x0d, 0x0b, 0x29, 0x77, 0xaa, 0x42, 0x45, 0xb1, 0xd3, 0x34, 0x93, - 0xb9, 0x3b, 0x50, 0x95, 0x62, 0xd4, 0x5b, 0x39, 0xa3, 0x94, 0x7e, 0xf7, 0x82, 0x4c, 0xa7, 0xee, - 0x54, 0x89, 0x56, 0xed, 0xa5, 0x29, 0xd2, 0x13, 0xd9, 0x06, 0x22, 0xdc, 0xc4, 0xe7, 0x2e, 0xed, - 0xba, 0x51, 0x14, 0x84, 0xdd, 0x97, 0x21, 0x67, 0x4d, 0x53, 0x5d, 0x77, 0x63, 0x74, 0xbe, 0x62, - 0x1d, 0x68, 0xef, 0x96, 0x74, 0x7e, 0xf6, 0x7c, 0x7f, 0xcf, 0xb1, 0xc4, 0x24, 0x12, 0x72, 0x46, - 0x6e, 0x40, 0x95, 0x22, 0x8a, 0xae, 0xef, 0xb1, 0x93, 0x66, 0x55, 0xf5, 0x6b, 0x4a, 0xe0, 0x99, - 0xc7, 0x4e, 0xc8, 0x3d, 0xb0, 0x5c, 0x3f, 0x40, 0x97, 0x26, 0xdd, 0x74, 0x31, 0x68, 0x13, 0x56, - 0x8d, 0x35, 0xd3, 0xa9, 0xa7, 0x78, 0x2a, 0x25, 0x4a, 0x6e, 0xc3, 0x5c, 0x36, 0xc4, 0x61, 0x1c, - 0x21, 0x6d, 0xd6, 0x14, 0x2f, 0x9b, 0xec, 0xae, 0xc4, 0xc8, 0x7f, 0x81, 0x4c, 0x8d, 0x43, 0x33, - 0x67, 0x15, 0x73, 0x7e, 0xd2, 0xa3, 0xe9, 0xb7, 0x60, 0xf6, 0xd8, 0xa3, 0xd8, 0x15, 0x01, 0x9e, - 0x7a, 0x78, 0xd6, 0x9c, 0x53, 0xc4, 0x9a, 0xc4, 0x0e, 0x34, 0x64, 0xff, 0x64, 0x80, 0x99, 0xdd, - 0x0c, 0xa9, 0xc1, 0xcc, 0x0b, 0x76, 0xc2, 0xf8, 0x19, 0xb3, 0xae, 0x48, 0x23, 0xdd, 0x55, 0xcb, - 0x20, 0x0b, 0x50, 0x57, 0x2b, 0xb6, 0xc3, 0x4e, 0xbd, 0x48, 0x55, 0xb0, 0x66, 0xc8, 0x0d, 0xb8, - 0xfe, 0x70, 0xa2, 0xa6, 0x1c, 0xcc, 0xc3, 0x63, 0x97, 0x0d, 0x90, 0x5a, 0x26, 0x59, 0x84, 0xf9, - 0x5d, 0x35, 0xb6, 0x49, 0xb8, 0x4a, 0x9a, 0xd0, 0xd0, 0x70, 0x07, 0x23, 0xd7, 0xf3, 0xc3, 0xcc, - 0x53, 0xb3, 0x4b, 0x66, 0xc1, 0x2a, 0xd8, 0x25, 0xb3, 0x68, 0x15, 0xed, 0x92, 0x59, 0xb2, 0x4a, - 0x76, 0xc9, 0x2c, 0x5b, 0x65, 0xbb, 0x64, 0x56, 0xac, 0x8a, 0x5d, 0x32, 0xc1, 0x02, 0xfb, 0xc7, - 0x02, 0xcc, 0x3e, 0xe6, 0xc1, 0xd0, 0xcd, 0x16, 0x65, 0x4a, 0x07, 0xc6, 0x3f, 0xd5, 0x41, 0x03, - 0xca, 0x91, 0x17, 0xf9, 0xd9, 0xe6, 0x68, 0x83, 0xb4, 0xc0, 0x0c, 0xe3, 0x9e, 0x76, 0xe8, 0x25, - 0xc9, 0x6d, 0x42, 0xa0, 0xd4, 0xe3, 0x34, 0x49, 0x57, 0x40, 0x9d, 0xa7, 0x95, 0x50, 0x7e, 0x43, - 0x09, 0x0d, 0x28, 0xeb, 0x61, 0x55, 0xd4, 0x0c, 0xb4, 0x71, 0x61, 0x40, 0x33, 0x17, 0x06, 0x44, - 0x3e, 0x78, 0x63, 0x73, 0x3d, 0x8a, 0x2c, 0xf2, 0x8e, 0x3c, 0x0c, 0xb4, 0x50, 0x9d, 0x6b, 0x93, - 0xee, 0x9d, 0xdc, 0xbb, 0xfe, 0x97, 0x01, 0x8d, 0xb7, 0x7d, 0x97, 0xc8, 0x35, 0x20, 0xea, 0x0e, - 0x24, 0xf0, 0x82, 0x51, 0x3c, 0xf2, 0x18, 0x52, 0xeb, 0x0a, 0x99, 0x87, 0xb9, 0x1c, 0xdf, 0xfd, - 0xfc, 0xf0, 0xd0, 0x32, 0xc8, 0x3d, 0xb8, 0x93, 0x43, 0x5b, 0x42, 0xf8, 0x28, 0xad, 0x3d, 0x2e, - 0x2b, 0xf4, 0x55, 0xbd, 0x34, 0xb9, 0x55, 0x20, 0xb7, 0x61, 0x25, 0xa7, 0x3e, 0xf6, 0x02, 0xec, - 0xb9, 0x21, 0x3e, 0xf4, 0x79, 0x4c, 0xb5, 0x88, 0x3c, 0x36, 0xb0, 0x8a, 0xe4, 0x3f, 0x70, 0x37, - 0x27, 0x7d, 0xe1, 0x31, 0xca, 0xcf, 0xc2, 0x77, 0x65, 0x2c, 0x91, 0x16, 0x5c, 0xcb, 0xc9, 0x4f, - 0x62, 0xf7, 0x0c, 0x3d, 0x69, 0x3e, 0xf5, 0x22, 0xab, 0x4c, 0x6c, 0x58, 0x1e, 0x37, 0x36, 0x74, - 0xbf, 0xe1, 0xac, 0x83, 0x32, 0x6c, 0x5c, 0xac, 0xb2, 0xfe, 0x35, 0x2c, 0xbc, 0xe5, 0x5b, 0x47, - 0x96, 0xf4, 0xcb, 0x26, 0xb3, 0x27, 0x6f, 0x60, 0x41, 0x7f, 0x26, 0x32, 0xd7, 0x33, 0x7e, 0x66, - 0x19, 0xd9, 0x75, 0x65, 0xe0, 0x9e, 0x94, 0x9f, 0x6f, 0x15, 0x36, 0x7f, 0x33, 0xa0, 0x36, 0x91, - 0x9f, 0xbc, 0x04, 0x18, 0x3f, 0x93, 0xc8, 0xe5, 0x6f, 0x9a, 0x9c, 0xd7, 0xce, 0xde, 0x52, 0xf7, - 0xdf, 0x9b, 0x2f, 0xdf, 0x5b, 0x5f, 0x42, 0x49, 0xbe, 0x21, 0xc8, 0xda, 0xe5, 0x51, 0x8c, 0xe6, - 0xf9, 0xff, 0xfd, 0x1e, 0x4c, 0xe1, 0x27, 0xdb, 0xed, 0x5f, 0x46, 0xcb, 0xc6, 0xaf, 0xa3, 0x65, - 0xe3, 0x8f, 0xd1, 0xb2, 0xf1, 0xd5, 0x6a, 0x0f, 0x83, 0x28, 0x69, 0x47, 0xd8, 0x3f, 0xde, 0x48, - 0xe3, 0x37, 0xc4, 0xc9, 0x60, 0xfc, 0xaa, 0xec, 0x55, 0xd4, 0x7b, 0xf1, 0xc1, 0xdf, 0x01, 0x00, - 0x00, 0xff, 0xff, 0x49, 0x51, 0xb6, 0x09, 0x73, 0x0a, 0x00, 0x00, + 0x16, 0x0e, 0x25, 0x4a, 0xa6, 0x8e, 0xec, 0x88, 0x1e, 0xcb, 0x89, 0xac, 0x20, 0xb6, 0xc3, 0xdc, + 0xdc, 0x38, 0xbe, 0xb9, 0x32, 0xae, 0x03, 0xdc, 0x02, 0xed, 0xa2, 0xb5, 0xa3, 0xa4, 0x71, 0x13, + 0xff, 0x94, 0x71, 0xd0, 0xa2, 0x40, 0x41, 0x50, 0x9a, 0x63, 0x99, 0x31, 0x35, 0x33, 0xe1, 0x8f, + 0x1d, 0x76, 0xd5, 0x75, 0x1f, 0xa1, 0x2f, 0x50, 0x14, 0x5d, 0xf4, 0x35, 0xba, 0x2c, 0xd0, 0x75, + 0x8d, 0x42, 0x8f, 0xd1, 0x45, 0x51, 0xcc, 0xf0, 0x47, 0x52, 0x9c, 0x18, 0x41, 0x57, 0x9a, 0xf3, + 0x9d, 0xef, 0xfc, 0x68, 0xce, 0x77, 0xc0, 0x81, 0x25, 0x11, 0x87, 0xc7, 0x51, 0x22, 0x30, 0xdc, + 0x28, 0x4e, 0x1d, 0x11, 0xf0, 0x88, 0x93, 0xc6, 0x19, 0x86, 0xc7, 0x0c, 0xa3, 0x8e, 0x74, 0x74, + 0x4e, 0xff, 0xd7, 0x6e, 0x0e, 0xf8, 0x80, 0x2b, 0xdf, 0x86, 0x3c, 0xa5, 0x34, 0xeb, 0x67, 0x0d, + 0x16, 0x0f, 0xe2, 0xf0, 0xf8, 0x39, 0x06, 0xa7, 0x5e, 0x1f, 0xe5, 0x0f, 0x06, 0x3b, 0xec, 0x88, + 0xb7, 0x6b, 0x30, 0x63, 0xe3, 0xab, 0x18, 0xc3, 0xa8, 0xfd, 0x9d, 0x06, 0x15, 0x1b, 0x85, 0x9f, + 0x90, 0x9b, 0x00, 0x22, 0xee, 0xf9, 0x5e, 0xdf, 0x39, 0xc1, 0xa4, 0xa5, 0xad, 0x6a, 0x6b, 0xb3, + 0x76, 0x2d, 0x45, 0x9e, 0x62, 0x42, 0x7a, 0xb0, 0x18, 0xc6, 0x42, 0xf0, 0x20, 0x42, 0xea, 0x44, + 0xfc, 0x04, 0x99, 0xa3, 0x7a, 0x6a, 0x95, 0x56, 0xcb, 0x6b, 0xf5, 0xcd, 0x4e, 0xe7, 0x8d, 0xa6, + 0x3a, 0x93, 0xa5, 0xf3, 0xc0, 0x43, 0x19, 0x77, 0x98, 0x08, 0xb4, 0x17, 0xc2, 0x0b, 0x58, 0x68, + 0x7d, 0xaf, 0xc1, 0xcd, 0x4b, 0xc3, 0xc8, 0x03, 0x98, 0x73, 0x85, 0x70, 0x7a, 0x31, 0xa3, 0x3e, + 0x3a, 0x1e, 0x55, 0x7d, 0xd6, 0xb6, 0x1b, 0xa3, 0xf3, 0x95, 0xfa, 0x96, 0x10, 0xdb, 0x0a, 0xdf, + 0xe9, 0xda, 0x75, 0xb7, 0x30, 0x28, 0xe9, 0x02, 0x8c, 0x1b, 0x6e, 0x95, 0x56, 0xb5, 0xb5, 0xab, + 0x9b, 0x77, 0x2e, 0xeb, 0x77, 0xdc, 0x66, 0x2d, 0xca, 0x8f, 0xd6, 0x9f, 0x1a, 0x34, 0xa6, 0xae, + 0x93, 0xd1, 0xf6, 0xef, 0x5a, 0x71, 0x93, 0xe4, 0x31, 0x18, 0xc8, 0x4e, 0xd1, 0xe7, 0x02, 0x55, + 0x57, 0xf5, 0xcd, 0xf5, 0x0b, 0x35, 0xf6, 0xe3, 0x68, 0xff, 0xe8, 0x79, 0xc4, 0x03, 0xdc, 0xc5, + 0x30, 0x74, 0x07, 0xf8, 0x28, 0x8b, 0xb0, 0x8b, 0x58, 0xf2, 0x09, 0x18, 0x22, 0xf0, 0x78, 0xe0, + 0x45, 0x49, 0xd6, 0xeb, 0xbf, 0x2e, 0xeb, 0xf5, 0x20, 0xe3, 0xda, 0x45, 0x14, 0x79, 0x02, 0xb5, + 0x00, 0xfb, 0xe8, 0x9d, 0x62, 0x10, 0xb6, 0xca, 0x6a, 0x3c, 0xeb, 0x97, 0xa5, 0xd8, 0x17, 0xee, + 0xab, 0x18, 0xed, 0x2c, 0xc4, 0x1e, 0x07, 0xb7, 0x67, 0x32, 0x71, 0x58, 0xdf, 0x6a, 0xb0, 0xf4, + 0xce, 0xe6, 0x49, 0x13, 0x2a, 0x8c, 0xb3, 0x3e, 0x66, 0xaa, 0x49, 0x0d, 0x62, 0x42, 0xb9, 0xc7, + 0x5f, 0xab, 0xff, 0x30, 0x6b, 0xcb, 0x23, 0xf9, 0x08, 0x1a, 0x83, 0x80, 0xc7, 0xc2, 0x09, 0xf0, + 0x08, 0x03, 0x94, 0x11, 0xba, 0xf4, 0x6e, 0x93, 0xd1, 0xf9, 0xca, 0xd5, 0x4f, 0xa5, 0xcb, 0xce, + 0x3d, 0xf6, 0xd5, 0xc1, 0x94, 0x6d, 0x7d, 0x0c, 0x8b, 0xe3, 0x0e, 0x1e, 0xbd, 0x16, 0x3c, 0x44, + 0xda, 0x75, 0x23, 0xf7, 0x7d, 0xab, 0x5b, 0x2e, 0x2c, 0xbd, 0xf3, 0x4f, 0x93, 0x5b, 0x30, 0xcb, + 0x15, 0x92, 0x6a, 0x3b, 0xcb, 0x55, 0x4f, 0x31, 0xa5, 0x07, 0x49, 0x09, 0xd3, 0x58, 0xc7, 0xa5, + 0x34, 0x50, 0xa9, 0x6b, 0x76, 0x3d, 0xc3, 0xb6, 0x28, 0x0d, 0xac, 0x1f, 0xaa, 0x30, 0xd7, 0xc5, + 0x7e, 0x90, 0x88, 0x08, 0xa9, 0x2c, 0x46, 0xee, 0x03, 0xb8, 0xfd, 0x3e, 0x8f, 0x59, 0x34, 0x56, + 0xeb, 0xdc, 0xe8, 0x7c, 0xa5, 0xb6, 0x95, 0xa2, 0x3b, 0x5d, 0xbb, 0x96, 0x11, 0x76, 0xa8, 0x2c, + 0x91, 0xb3, 0x99, 0x3b, 0xc4, 0xbc, 0x44, 0x86, 0xed, 0xb9, 0x43, 0x24, 0xff, 0x87, 0xeb, 0x7d, + 0xce, 0xe4, 0x74, 0xdc, 0xc8, 0xe3, 0xcc, 0x99, 0xd8, 0xd9, 0xb2, 0x62, 0x2f, 0x4e, 0xba, 0x0f, + 0x8a, 0xfd, 0xfd, 0x10, 0x96, 0xa6, 0xe2, 0xa8, 0x17, 0x0a, 0xdf, 0x4d, 0xd2, 0x3a, 0xba, 0x8a, + 0x9c, 0x4a, 0xdc, 0x4d, 0xfd, 0xaa, 0xe6, 0x3a, 0xcc, 0x0f, 0x71, 0xd8, 0xc3, 0x60, 0xb2, 0x5a, + 0x45, 0xc5, 0x34, 0x52, 0xc7, 0xb8, 0x4e, 0x07, 0x16, 0x32, 0xee, 0x54, 0x85, 0xaa, 0x62, 0x67, + 0x69, 0x26, 0x73, 0x77, 0xa1, 0x26, 0x25, 0x99, 0xee, 0xe6, 0x8c, 0xd2, 0xfb, 0xdd, 0x0b, 0x62, + 0x9d, 0xba, 0x53, 0x25, 0x5d, 0xb5, 0x9d, 0x86, 0xc8, 0x4e, 0x64, 0x1b, 0x88, 0x70, 0x13, 0x9f, + 0xbb, 0xd4, 0x71, 0xa3, 0x28, 0x08, 0x9d, 0x97, 0x21, 0x67, 0x2d, 0x43, 0x5d, 0x77, 0x73, 0x74, + 0xbe, 0x62, 0x1e, 0xa4, 0xde, 0x2d, 0xe9, 0xfc, 0xec, 0xf9, 0xfe, 0x9e, 0x6d, 0x8a, 0x49, 0x24, + 0xe4, 0x8c, 0xdc, 0x80, 0x1a, 0x45, 0x14, 0x8e, 0xef, 0xb1, 0x93, 0x56, 0x4d, 0xf5, 0x6b, 0x48, + 0xe0, 0x99, 0xc7, 0x4e, 0xc8, 0x3d, 0x30, 0x5d, 0x3f, 0x40, 0x97, 0x26, 0x4e, 0xb6, 0x1e, 0xb4, + 0x05, 0xab, 0xda, 0x9a, 0x61, 0x37, 0x32, 0x3c, 0x93, 0x12, 0x25, 0xb7, 0x61, 0x2e, 0x1f, 0xe2, + 0x30, 0x8e, 0x90, 0xb6, 0xea, 0x8a, 0x97, 0x4f, 0x76, 0x57, 0x62, 0xe4, 0xbf, 0x40, 0xa6, 0xc6, + 0x91, 0x32, 0x67, 0x15, 0x73, 0x7e, 0xd2, 0x93, 0xd2, 0x6f, 0xc1, 0xec, 0xb1, 0x47, 0xd1, 0x11, + 0x01, 0x9e, 0x7a, 0x78, 0xd6, 0x9a, 0x53, 0xc4, 0xba, 0xc4, 0x0e, 0x52, 0xc8, 0xfa, 0x49, 0x03, + 0x23, 0xbf, 0x19, 0x52, 0x87, 0x99, 0x17, 0xec, 0x84, 0xf1, 0x33, 0x66, 0x5e, 0x91, 0x46, 0xb6, + 0xb1, 0xa6, 0x46, 0x16, 0xa0, 0xa1, 0x16, 0x6d, 0x87, 0x9d, 0x7a, 0x91, 0xaa, 0x60, 0xce, 0x90, + 0x1b, 0x70, 0xfd, 0xe1, 0x44, 0x4d, 0x39, 0x98, 0x87, 0xc7, 0x2e, 0x1b, 0x20, 0x35, 0x0d, 0xb2, + 0x08, 0xf3, 0xbb, 0x6a, 0x6c, 0x93, 0x70, 0x8d, 0xb4, 0xa0, 0x99, 0xc2, 0x5d, 0x8c, 0x5c, 0xcf, + 0x0f, 0x73, 0x4f, 0xdd, 0xd2, 0x8d, 0x92, 0x59, 0xb2, 0x74, 0xa3, 0x6c, 0x96, 0x2d, 0xdd, 0xd0, + 0x4d, 0xdd, 0xd2, 0x8d, 0x8a, 0x59, 0xb1, 0x74, 0xa3, 0x6a, 0x56, 0x2d, 0xdd, 0x00, 0x13, 0xac, + 0x1f, 0x4b, 0x30, 0xfb, 0x98, 0x07, 0x43, 0x37, 0x5f, 0x94, 0x29, 0x1d, 0x68, 0xff, 0x54, 0x07, + 0x4d, 0xa8, 0x44, 0x5e, 0xe4, 0xe7, 0x9b, 0x93, 0x1a, 0xa4, 0x0d, 0x46, 0x18, 0xf7, 0x52, 0x47, + 0xba, 0x24, 0x85, 0x4d, 0x08, 0xe8, 0x3d, 0x4e, 0x93, 0x6c, 0x05, 0xd4, 0x79, 0x5a, 0x09, 0x95, + 0x37, 0x94, 0xd0, 0x84, 0x4a, 0x3a, 0xac, 0xaa, 0x9a, 0x41, 0x6a, 0x5c, 0x18, 0xd0, 0xcc, 0x85, + 0x01, 0x91, 0x0f, 0xde, 0xd8, 0x5c, 0x8f, 0x22, 0x8b, 0xbc, 0x23, 0x0f, 0x83, 0x54, 0xa8, 0xf6, + 0xb5, 0x49, 0xf7, 0x4e, 0xe1, 0x5d, 0xff, 0x4b, 0x83, 0xe6, 0xdb, 0xbe, 0x4e, 0xe4, 0x1a, 0x10, + 0x75, 0x07, 0x12, 0x78, 0xc1, 0x28, 0x1e, 0x79, 0x0c, 0xa9, 0x79, 0x85, 0xcc, 0xc3, 0x5c, 0x81, + 0xef, 0x7e, 0x7e, 0x78, 0x68, 0x6a, 0xe4, 0x1e, 0xdc, 0x29, 0xa0, 0x2d, 0x21, 0x7c, 0x94, 0xd6, + 0x1e, 0x97, 0x15, 0xfa, 0xaa, 0x5e, 0x96, 0xdc, 0x2c, 0x91, 0xdb, 0xb0, 0x52, 0x50, 0x1f, 0x7b, + 0x01, 0xf6, 0xdc, 0x10, 0x1f, 0xfa, 0x3c, 0xa6, 0xa9, 0x88, 0x3c, 0x36, 0x30, 0xcb, 0xe4, 0x3f, + 0x70, 0xb7, 0x20, 0x7d, 0xe1, 0x31, 0xca, 0xcf, 0xc2, 0x77, 0x65, 0xd4, 0x49, 0x1b, 0xae, 0x15, + 0xe4, 0x27, 0xb1, 0x7b, 0x86, 0x9e, 0x34, 0x9f, 0x7a, 0x91, 0x59, 0x21, 0x16, 0x2c, 0x8f, 0x1b, + 0x1b, 0xba, 0xdf, 0x70, 0xd6, 0x45, 0x19, 0x36, 0x2e, 0x56, 0x5d, 0xff, 0x1a, 0x16, 0xde, 0xf2, + 0xc5, 0x23, 0x4b, 0xe9, 0xfb, 0x26, 0xb7, 0x27, 0x6f, 0x60, 0x21, 0xfd, 0x56, 0xe7, 0xae, 0x67, + 0xfc, 0xcc, 0xd4, 0xf2, 0xeb, 0xca, 0xc1, 0x3d, 0x29, 0x3f, 0xdf, 0x2c, 0x6d, 0xfe, 0xa6, 0x41, + 0x7d, 0x22, 0x3f, 0x79, 0x09, 0x30, 0x7e, 0x2c, 0x91, 0xcb, 0x5f, 0x36, 0x05, 0xaf, 0x93, 0xbf, + 0xa8, 0xee, 0xbf, 0x37, 0x5f, 0xbe, 0xba, 0xbe, 0x04, 0x5d, 0xbe, 0x24, 0xc8, 0xda, 0xe5, 0x51, + 0x8c, 0x16, 0xf9, 0xff, 0xfd, 0x1e, 0x4c, 0xe1, 0x27, 0xdb, 0x9d, 0x5f, 0x46, 0xcb, 0xda, 0xaf, + 0xa3, 0x65, 0xed, 0x8f, 0xd1, 0xb2, 0xf6, 0xd5, 0x6a, 0x0f, 0x83, 0x28, 0xe9, 0x44, 0xd8, 0x3f, + 0xde, 0xc8, 0xe2, 0x37, 0xc4, 0xc9, 0x60, 0xfc, 0xb6, 0xec, 0x55, 0xd5, 0xab, 0xf1, 0xc1, 0xdf, + 0x01, 0x00, 0x00, 0xff, 0xff, 0x23, 0xe0, 0x7f, 0x2c, 0x79, 0x0a, 0x00, 0x00, } func (m *PushServiceServerInfo) Marshal() (dAtA []byte, err error) { @@ -1286,7 +1286,7 @@ func (m *OutOfStoreMessageEnvelope) MarshalToSizedBuffer(dAtA []byte) (int, erro return len(dAtA) - i, nil } -func (m *PushExposedData) Marshal() (dAtA []byte, err error) { +func (m *OutOfStoreExposedData) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) @@ -1296,12 +1296,12 @@ func (m *PushExposedData) Marshal() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *PushExposedData) MarshalTo(dAtA []byte) (int, error) { +func (m *OutOfStoreExposedData) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *PushExposedData) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *OutOfStoreExposedData) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int @@ -1732,7 +1732,7 @@ func (m *OutOfStoreMessageEnvelope) Size() (n int) { return n } -func (m *PushExposedData) Size() (n int) { +func (m *OutOfStoreExposedData) Size() (n int) { if m == nil { return 0 } @@ -2596,7 +2596,7 @@ func (m *OutOfStoreMessageEnvelope) Unmarshal(dAtA []byte) error { } return nil } -func (m *PushExposedData) Unmarshal(dAtA []byte) error { +func (m *OutOfStoreExposedData) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -2619,10 +2619,10 @@ func (m *PushExposedData) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: PushExposedData: wiretype end group for non-group") + return fmt.Errorf("proto: OutOfStoreExposedData: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: PushExposedData: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: OutOfStoreExposedData: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: diff --git a/pkg/secretstore/chain_key.go b/pkg/secretstore/chain_key.go new file mode 100644 index 00000000..58d3f348 --- /dev/null +++ b/pkg/secretstore/chain_key.go @@ -0,0 +1,87 @@ +package secretstore + +import ( + crand "crypto/rand" + "fmt" + + "github.com/libp2p/go-libp2p/core/crypto" + "golang.org/x/crypto/nacl/box" + + "berty.tech/weshnet/pkg/cryptoutil" + "berty.tech/weshnet/pkg/errcode" + "berty.tech/weshnet/pkg/protocoltypes" +) + +// newDeviceChainKey creates a new random chain key +func newDeviceChainKey() (*protocoltypes.DeviceChainKey, error) { + chainKey := make([]byte, 32) + _, err := crand.Read(chainKey) + if err != nil { + return nil, errcode.ErrCryptoRandomGeneration.Wrap(err) + } + + return &protocoltypes.DeviceChainKey{ + ChainKey: chainKey, + Counter: 0, + }, nil +} + +// encryptDeviceChainKey encrypts a device chain key for a target member +func encryptDeviceChainKey(localDevicePrivateKey crypto.PrivKey, remoteMemberPubKey crypto.PubKey, deviceChainKey *protocoltypes.DeviceChainKey, group *protocoltypes.Group) ([]byte, error) { + chainKeyBytes, err := deviceChainKey.Marshal() + if err != nil { + return nil, errcode.ErrSerialization.Wrap(err) + } + + mongPriv, mongPub, err := cryptoutil.EdwardsToMontgomery(localDevicePrivateKey, remoteMemberPubKey) + if err != nil { + return nil, errcode.ErrCryptoKeyConversion.Wrap(err) + } + + nonce := groupIDToNonce(group) + encryptedChainKey := box.Seal(nil, chainKeyBytes, nonce, mongPub, mongPriv) + + return encryptedChainKey, nil +} + +// decryptDeviceChainKey decrypts a chain key sent by the given device +func decryptDeviceChainKey(encryptedDeviceChainKey []byte, group *protocoltypes.Group, localMemberPrivateKey crypto.PrivKey, senderDevicePubKey crypto.PubKey) (*protocoltypes.DeviceChainKey, error) { + mongPriv, mongPub, err := cryptoutil.EdwardsToMontgomery(localMemberPrivateKey, senderDevicePubKey) + if err != nil { + return nil, errcode.ErrCryptoKeyConversion.Wrap(err) + } + + nonce := groupIDToNonce(group) + decryptedSecret := &protocoltypes.DeviceChainKey{} + decryptedMessage, ok := box.Open(nil, encryptedDeviceChainKey, nonce, mongPub, mongPriv) + if !ok { + return nil, errcode.ErrCryptoDecrypt.Wrap(fmt.Errorf("unable to decrypt message")) + } + + err = decryptedSecret.Unmarshal(decryptedMessage) + if err != nil { + return nil, errcode.ErrDeserialization.Wrap(err) + } + + return decryptedSecret, nil +} + +// groupIDToNonce converts a group public key to a value which can be used as +// a nonce of the nacl library +func groupIDToNonce(group *protocoltypes.Group) *[cryptoutil.NonceSize]byte { + // Nonce doesn't need to be secret, random nor unpredictable, it just needs + // to be used only once for a given sender+receiver set, and we will send + // only one SecretEntryPayload per localDevicePrivateKey+remoteMemberPubKey + // So we can reuse groupID as nonce for all SecretEntryPayload and save + // 24 bytes of storage and bandwidth for each of them. + // + // See https://pynacl.readthedocs.io/en/stable/secret/#nonce + // See Security Model here: https://nacl.cr.yp.to/box.html + var nonce [cryptoutil.NonceSize]byte + + gid := group.GetPublicKey() + + copy(nonce[:], gid) + + return &nonce +} diff --git a/pkg/secretstore/datastore_keys.go b/pkg/secretstore/datastore_keys.go new file mode 100644 index 00000000..24a805f8 --- /dev/null +++ b/pkg/secretstore/datastore_keys.go @@ -0,0 +1,117 @@ +package secretstore + +import ( + "encoding/base64" + "encoding/hex" + "fmt" + + "github.com/ipfs/go-cid" + "github.com/ipfs/go-datastore" + "github.com/libp2p/go-libp2p/core/crypto" + + "berty.tech/weshnet/pkg/errcode" +) + +const ( + // dsNamespaceChainKeyForDeviceOnGroup is a namespace stores the current + // state of a device chain key for a given group. + // It contains the secret used to derive the next value of the chain key + // and used to generate a message key for the message at `counter` value, + // then put in the dsNamespacePrecomputedMessageKeys namespace. + dsNamespaceChainKeyForDeviceOnGroup = "chainKeyForDeviceOnGroup" + + // dsNamespacePrecomputedMessageKeys is a namespace storing precomputed + // message keys for a given group, device and message counter. + // As the chain key stored has already been derived, these message keys + // need to be computed beforehand. + // The corresponding message can then be decrypted via a quick lookup. + dsNamespacePrecomputedMessageKeys = "precomputedMessageKeys" + + // dsNamespaceMessageKeyForCIDs is a namespace containing the message key + // for a given CID once the corresponding message has been decrypted. + dsNamespaceMessageKeyForCIDs = "messageKeyForCIDs" + + // dsNamespaceOutOfStoreGroupHint is a namespace where HMAC value are + // associated to a group public key. + // It is used when receiving an out-of-store message (e.g. a push + // notification) to identify the group on which the message belongs, which + // can then be decrypted. + dsNamespaceOutOfStoreGroupHint = "outOfStoreGroupHint" + + // dsNamespaceOutOfStoreGroupHintCounters is a namespace storing first and + // last counter values for generated group hints inside the + // dsNamespaceOutOfStoreGroupHint namespace + dsNamespaceOutOfStoreGroupHintCounters = "outOfStoreGroupHintCounters" + + // dsNamespaceGroupDatastore is a namespace to store groups by their public + // key + dsNamespaceGroupDatastore = "groupByPublicKey" +) + +func dsKeyForGroup(key []byte) datastore.Key { + return datastore.KeyWithNamespaces([]string{ + dsNamespaceGroupDatastore, + base64.RawURLEncoding.EncodeToString(key), + }) +} + +// dsKeyForPrecomputedMessageKey returns a datastore.Key where will be stored a +// precalculated message key for a given group and device +func dsKeyForPrecomputedMessageKey(groupPublicKey, devicePublicKey []byte, counter uint64) datastore.Key { + return datastore.KeyWithNamespaces([]string{ + dsNamespacePrecomputedMessageKeys, + hex.EncodeToString(groupPublicKey), + hex.EncodeToString(devicePublicKey), + fmt.Sprintf("%d", counter), + }) +} + +// dsKeyForCurrentChainKey returns a datastore.Key where will be stored a +// device chain key for a given group. +func dsKeyForCurrentChainKey(groupPublicKey crypto.PubKey, devicePublicKey crypto.PubKey) (datastore.Key, error) { + devicePublicKeyBytes, err := devicePublicKey.Raw() + if err != nil { + return datastore.Key{}, errcode.ErrSerialization.Wrap(err) + } + + groupPublicKeyBytes, err := groupPublicKey.Raw() + if err != nil { + return datastore.Key{}, errcode.ErrSerialization.Wrap(err) + } + + return datastore.KeyWithNamespaces([]string{ + dsNamespaceChainKeyForDeviceOnGroup, + hex.EncodeToString(groupPublicKeyBytes), + hex.EncodeToString(devicePublicKeyBytes), + }), nil +} + +// dsKeyForMessageKeyByCID returns a datastore.Key where will be stored a +// message decryption key for a given message CID. +func dsKeyForMessageKeyByCID(id cid.Cid) datastore.Key { + // TODO: specify the id + return datastore.KeyWithNamespaces([]string{ + dsNamespaceMessageKeyForCIDs, + id.String(), + }) +} + +// dsKeyForOutOfStoreMessageGroupHint returns a datastore.Key where will be +// stored a group public key for a given push group reference. +func dsKeyForOutOfStoreMessageGroupHint(ref []byte) datastore.Key { + return datastore.KeyWithNamespaces([]string{ + dsNamespaceOutOfStoreGroupHint, + base64.RawURLEncoding.EncodeToString(ref), + }) +} + +// dsKeyForOutOfStoreFirstLastCounters returns the datastore.Key where will be +// stored a protocoltypes.FirstLastCounters struct for the given group public +// key and device public key. +func dsKeyForOutOfStoreFirstLastCounters(groupPK, devicePK []byte) datastore.Key { + return datastore.KeyWithNamespaces([]string{ + dsNamespaceOutOfStoreGroupHintCounters, + base64.RawURLEncoding.EncodeToString(groupPK), + base64.RawURLEncoding.EncodeToString(devicePK), + }) +} diff --git a/pkg/secretstore/device_keystore_wrapper.go b/pkg/secretstore/device_keystore_wrapper.go new file mode 100644 index 00000000..79477ec3 --- /dev/null +++ b/pkg/secretstore/device_keystore_wrapper.go @@ -0,0 +1,259 @@ +package secretstore + +import ( + "crypto/ed25519" + crand "crypto/rand" + "encoding/hex" + "fmt" + "strings" + "sync" + + "github.com/aead/ecdh" + keystore "github.com/ipfs/go-ipfs-keystore" + "github.com/libp2p/go-libp2p/core/crypto" + "go.uber.org/zap" + + "berty.tech/weshnet/pkg/cryptoutil" + "berty.tech/weshnet/pkg/errcode" + "berty.tech/weshnet/pkg/protocoltypes" +) + +const ( + keyAccount = "accountSK" + keyAccountProof = "accountProofSK" + keyDevice = "deviceSK" + keyMemberDevice = "memberDeviceSK" + keyMember = "memberSK" + keyContactGroup = "contactGroupSK" +) + +// deviceKeystore is a wrapper around a keystore.Keystore object. +// It contains methods to manipulate member and device keys. +type deviceKeystore struct { + keystore keystore.Keystore + mu sync.Mutex + logger *zap.Logger +} + +// newDeviceKeystore instantiate a new device keystore +func newDeviceKeystore(ks keystore.Keystore, logger *zap.Logger) *deviceKeystore { + if logger == nil { + logger = zap.NewNop() + } + + return &deviceKeystore{ + keystore: ks, + logger: logger, + } +} + +// getAccountPrivateKey returns the private key of the current account +func (a *deviceKeystore) getAccountPrivateKey() (crypto.PrivKey, error) { + a.mu.Lock() + defer a.mu.Unlock() + + return a.getOrGenerateNamedKey(keyAccount) +} + +// getAccountProofPrivateKey returns the private proof key of +// the current account +func (a *deviceKeystore) getAccountProofPrivateKey() (crypto.PrivKey, error) { + a.mu.Lock() + defer a.mu.Unlock() + + return a.getOrGenerateNamedKey(keyAccountProof) +} + +// devicePrivateKey returns the current private key of the current device for +// the account and one-to-one conversations +func (a *deviceKeystore) devicePrivateKey() (crypto.PrivKey, error) { + a.mu.Lock() + defer a.mu.Unlock() + + return a.getOrGenerateNamedKey(keyDevice) +} + +// contactGroupPrivateKey retrieves the key for the contact group +// shared with the supplied contact's public key, this key will be derived to +// form the contact group keys +func (a *deviceKeystore) contactGroupPrivateKey(contactPublicKey crypto.PubKey) (crypto.PrivKey, error) { + accountPrivateKey, err := a.getAccountPrivateKey() + if err != nil { + return nil, errcode.ErrInternal.Wrap(err) + } + + return a.getOrComputeECDH(keyContactGroup, contactPublicKey, accountPrivateKey) +} + +// memberDeviceForMultiMemberGroup retrieves the device private key for the +// supplied group +func (a *deviceKeystore) memberDeviceForMultiMemberGroup(groupPublicKey crypto.PubKey) (*ownMemberDevice, error) { + memberPrivateKey, err := a.computeMemberKeyForMultiMemberGroup(groupPublicKey) + if err != nil { + return nil, errcode.ErrInternal.Wrap(fmt.Errorf("unable to get or generate a device key for group member: %w", err)) + } + + devicePrivateKey, err := a.getOrGenerateDeviceKeyForMultiMemberGroup(groupPublicKey) + if err != nil { + return nil, errcode.ErrInternal.Wrap(err) + } + + return newOwnMemberDevice(memberPrivateKey, devicePrivateKey), nil +} + +// memberDeviceForGroup computes or retrieves the member and device key for the +// supplied group +func (a *deviceKeystore) memberDeviceForGroup(group *protocoltypes.Group) (*ownMemberDevice, error) { + publicKey, err := group.GetPubKey() + if err != nil { + return nil, errcode.ErrInvalidInput.Wrap(fmt.Errorf("unable to get public key for group: %w", err)) + } + + switch group.GetGroupType() { + case protocoltypes.GroupTypeAccount, protocoltypes.GroupTypeContact: + memberPrivateKey, err := a.getAccountPrivateKey() + if err != nil { + return nil, errcode.ErrInternal.Wrap(err) + } + + devicePrivateKey, err := a.devicePrivateKey() + if err != nil { + return nil, errcode.ErrInternal.Wrap(err) + } + + return newOwnMemberDevice(memberPrivateKey, devicePrivateKey), nil + + case protocoltypes.GroupTypeMultiMember: + return a.memberDeviceForMultiMemberGroup(publicKey) + } + + return nil, errcode.ErrInvalidInput.Wrap(fmt.Errorf("unknown group type")) +} + +// getOrGenerateNamedKey retrieves a private key by its name, or generate it +// if missing +func (a *deviceKeystore) getOrGenerateNamedKey(name string) (crypto.PrivKey, error) { + privateKey, err := a.keystore.Get(name) + if err == nil { + return privateKey, nil + } else if err.Error() != keystore.ErrNoSuchKey.Error() { + return nil, errcode.ErrDBRead.Wrap(fmt.Errorf("unable to perform get operation on keystore: %w", err)) + } + + privateKey, _, err = crypto.GenerateEd25519Key(crand.Reader) + if err != nil { + return nil, errcode.ErrCryptoKeyGeneration.Wrap(fmt.Errorf("unable to generate an ed25519 key: %w", err)) + } + + if err := a.keystore.Put(name, privateKey); err != nil { + return nil, errcode.ErrDBWrite.Wrap(fmt.Errorf("unable to perform put operation on keystore: %w", err)) + } + + return privateKey, nil +} + +// getOrGenerateDeviceKeyForMultiMemberGroup fetches or generate a new device +// key for a multi-member group. The results do not need to be deterministic +// as it will only be used on the current device. +func (a *deviceKeystore) getOrGenerateDeviceKeyForMultiMemberGroup(groupPublicKey crypto.PubKey) (crypto.PrivKey, error) { + a.mu.Lock() + defer a.mu.Unlock() + + groupPublicKeyRaw, err := groupPublicKey.Raw() + if err != nil { + return nil, errcode.ErrSerialization.Wrap(err) + } + + name := strings.Join([]string{keyMemberDevice, hex.EncodeToString(groupPublicKeyRaw)}, "_") + + return a.getOrGenerateNamedKey(name) +} + +// getOrComputeECDH fetches a named private key or computes one via an +// elliptic-curve Diffie-Hellman key agreement if not cached +func (a *deviceKeystore) getOrComputeECDH(nameSpace string, publicKey crypto.PubKey, ownPrivateKey crypto.PrivKey) (crypto.PrivKey, error) { + a.mu.Lock() + defer a.mu.Unlock() + + publicKeyRaw, err := publicKey.Raw() + if err != nil { + return nil, errcode.ErrSerialization.Wrap(err) + } + + name := strings.Join([]string{nameSpace, hex.EncodeToString(publicKeyRaw)}, "_") + + privateKey, err := a.keystore.Get(name) + if err == nil { + return privateKey, nil + } else if err.Error() != keystore.ErrNoSuchKey.Error() { + return nil, errcode.ErrDBRead.Wrap(fmt.Errorf("unable to perform get operation on keystore: %w", err)) + } + + privateKeyBytes, publicKeyBytes, err := cryptoutil.EdwardsToMontgomery(ownPrivateKey, publicKey) + if err != nil { + return nil, errcode.ErrCryptoKeyConversion.Wrap(err) + } + + secret := ecdh.X25519().ComputeSecret(privateKeyBytes, publicKeyBytes) + groupSecretPrivateKey := ed25519.NewKeyFromSeed(secret) + + privateKey, _, err = crypto.KeyPairFromStdKey(&groupSecretPrivateKey) + if err != nil { + return nil, errcode.ErrCryptoKeyConversion.Wrap(err) + } + + if err := a.keystore.Put(name, privateKey); err != nil { + return nil, errcode.ErrDBWrite.Wrap(err) + } + + return privateKey, nil +} + +// computeMemberKeyForMultiMemberGroup returns a deterministic private key +// for a multi member group, this allows a group to be joined from two +// different devices simultaneously without requiring a consensus. +func (a *deviceKeystore) computeMemberKeyForMultiMemberGroup(groupPublicKey crypto.PubKey) (crypto.PrivKey, error) { + accountProofPrivateKey, err := a.getAccountProofPrivateKey() + if err != nil { + return nil, errcode.ErrInternal.Wrap(err) + } + + return a.getOrComputeECDH(keyMember, groupPublicKey, accountProofPrivateKey) +} + +// restoreAccountKeys restores exported LibP2P keys into the deviceKeystore, it +// will fail if accounts keys are already created or imported into the keystore +func (a *deviceKeystore) restoreAccountKeys(accountPrivateKeyBytes []byte, accountProofPrivateKeyBytes []byte) error { + privateKeys := map[string]crypto.PrivKey{} + + for keyName, keyBytes := range map[string][]byte{ + keyAccount: accountPrivateKeyBytes, + keyAccountProof: accountProofPrivateKeyBytes, + } { + var err error + privateKeys[keyName], err = getEd25519PrivateKeyFromLibP2PFormattedBytes(keyBytes) + if err != nil { + return errcode.ErrDeserialization.Wrap(err) + } + } + + if privateKeys[keyAccount].Equals(privateKeys[keyAccountProof]) { + return errcode.ErrInvalidInput.Wrap(fmt.Errorf("the account key cannot be the same value as the account proof key")) + } + + for keyName := range privateKeys { + if exists, err := a.keystore.Has(keyName); err != nil { + return errcode.ErrDBRead.Wrap(err) + } else if exists { + return errcode.ErrInvalidInput.Wrap(fmt.Errorf("an account is already set in this keystore")) + } + } + + for keyName, privateKey := range privateKeys { + if err := a.keystore.Put(keyName, privateKey); err != nil { + return errcode.ErrDBWrite.Wrap(err) + } + } + + return nil +} diff --git a/pkg/secretstore/device_keystore_wrapper_test.go b/pkg/secretstore/device_keystore_wrapper_test.go new file mode 100644 index 00000000..023f99fa --- /dev/null +++ b/pkg/secretstore/device_keystore_wrapper_test.go @@ -0,0 +1,207 @@ +package secretstore_test + +import ( + crand "crypto/rand" + "testing" + + "github.com/libp2p/go-libp2p/core/crypto" + "github.com/stretchr/testify/assert" + + "berty.tech/weshnet/pkg/protocoltypes" + "berty.tech/weshnet/pkg/secretstore" +) + +func Test_New_AccountPrivKey_AccountProofPrivKey(t *testing.T) { + acc, err := secretstore.NewInMemSecretStore(nil) + assert.NoError(t, err) + assert.NotNil(t, acc) + + sk1, skProof1, err := acc.ExportAccountKeysForBackup() + assert.NoError(t, err) + assert.NotNil(t, sk1) + assert.NotNil(t, skProof1) + + sk2, skProof2, err := acc.ExportAccountKeysForBackup() + assert.NoError(t, err) + assert.NotNil(t, sk2) + assert.NotNil(t, skProof2) + + assert.Equal(t, sk1, sk2) + assert.Equal(t, skProof1, skProof2) + + assert.NotEqual(t, sk1, skProof1) + assert.NotEqual(t, sk1, skProof2) + assert.NotEqual(t, sk2, skProof1) + assert.NotEqual(t, sk2, skProof2) +} + +func Test_ExportAccountKeys_ImportAccountKeys(t *testing.T) { + acc1, err := secretstore.NewInMemSecretStore(nil) + assert.NoError(t, err) + assert.NotNil(t, acc1) + + sk1, skProof1, err := acc1.ExportAccountKeysForBackup() + assert.NoError(t, err) + assert.NotNil(t, sk1) + assert.NotNil(t, skProof1) + + acc2, err := secretstore.NewInMemSecretStore(nil) + assert.NoError(t, err) + assert.NotNil(t, acc2) + + // Testing with a nil value + { + assert.Error(t, acc2.ImportAccountKeys(nil, skProof1)) + assert.Error(t, acc2.ImportAccountKeys(sk1, nil)) + } + + // Testing with an unsupported key format + { + invalidPriv, _, err := crypto.GenerateSecp256k1Key(crand.Reader) + assert.NoError(t, err) + + invalidPrivBytes, err := crypto.MarshalPrivateKey(invalidPriv) + assert.NoError(t, err) + + assert.Error(t, acc2.ImportAccountKeys(sk1, invalidPrivBytes)) + assert.Error(t, acc2.ImportAccountKeys(invalidPrivBytes, skProof1)) + } + + // Testing with an invalid key format + { + garbageBytes := []byte("garbage") + assert.Error(t, acc2.ImportAccountKeys(sk1, garbageBytes)) + assert.Error(t, acc2.ImportAccountKeys(garbageBytes, skProof1)) + } + + // Testing with account and proof key being the same + { + assert.Error(t, acc2.ImportAccountKeys(sk1, sk1)) + } + + // Valid test case + { + assert.NoError(t, acc2.ImportAccountKeys(sk1, skProof1)) + } + + // Attempting to import keys again + { + assert.Error(t, acc2.ImportAccountKeys(sk1, skProof1)) + } + + sk2, skProof2, err := acc1.ExportAccountKeysForBackup() + assert.NoError(t, err) + assert.NotNil(t, sk2) + assert.NotNil(t, skProof2) + + assert.Equal(t, sk1, sk2) + assert.Equal(t, skProof1, skProof2) + assert.NotEqual(t, sk1, skProof1) + assert.NotEqual(t, sk1, skProof2) + assert.NotEqual(t, sk2, skProof1) + assert.NotEqual(t, sk2, skProof2) +} + +func Test_DevicePrivKey(t *testing.T) { + acc1, err := secretstore.NewInMemSecretStore(nil) + assert.NoError(t, err) + assert.NotNil(t, acc1) + + sk1, skProof1, err := acc1.ExportAccountKeysForBackup() + assert.NoError(t, err) + assert.NotNil(t, sk1) + assert.NotNil(t, skProof1) + + acc2, err := secretstore.NewInMemSecretStore(nil) + assert.NoError(t, err) + assert.NotNil(t, acc1) + + err = acc2.ImportAccountKeys(sk1, skProof1) + assert.NoError(t, err) + + sk2, skProof2, err := acc2.ExportAccountKeysForBackup() + assert.NoError(t, err) + assert.NotNil(t, sk2) + assert.NotNil(t, skProof2) + + accGroup1, memberDevice1, err := acc1.GetGroupForAccount() + assert.NoError(t, err) + assert.NotNil(t, accGroup1) + + memberDevice2, err := acc2.GetOwnMemberDeviceForGroup(accGroup1) + assert.NoError(t, err) + assert.NotNil(t, memberDevice2) + + assert.True(t, memberDevice1.Member().Equals(memberDevice2.Member())) + assert.False(t, memberDevice1.Device().Equals(memberDevice2.Device())) +} + +func Test_ContactGroupPrivKey(t *testing.T) { + acc1, err := secretstore.NewInMemSecretStore(nil) + assert.NoError(t, err) + assert.NotNil(t, acc1) + + _, acc1MemberDevice, err := acc1.GetGroupForAccount() + assert.NoError(t, err) + assert.NotNil(t, acc1MemberDevice) + + acc2, err := secretstore.NewInMemSecretStore(nil) + assert.NoError(t, err) + assert.NotNil(t, acc2) + + _, acc2MemberDevice, err := acc2.GetGroupForAccount() + assert.NoError(t, err) + assert.NotNil(t, acc2MemberDevice) + + grp1, err := acc1.GetGroupForContact(acc2MemberDevice.Member()) + assert.NoError(t, err) + assert.NotNil(t, grp1) + + grp2, err := acc2.GetGroupForContact(acc1MemberDevice.Member()) + assert.NoError(t, err) + assert.NotNil(t, grp2) + + assert.Equal(t, grp1.PublicKey, grp2.PublicKey) + assert.Equal(t, grp1.Secret, grp2.Secret) + assert.Equal(t, grp1.GroupType, grp2.GroupType) +} + +func Test_MemberDeviceForGroup_multimember(t *testing.T) { + acc1, err := secretstore.NewInMemSecretStore(nil) + assert.NoError(t, err) + assert.NotNil(t, acc1) + + sk1, skProof1, err := acc1.ExportAccountKeysForBackup() + assert.NoError(t, err) + assert.NotNil(t, sk1) + assert.NotNil(t, skProof1) + + acc2, err := secretstore.NewInMemSecretStore(nil) + assert.NoError(t, err) + assert.NotNil(t, acc2) + + err = acc2.ImportAccountKeys(sk1, skProof1) + assert.NoError(t, err) + + sk2, skProof2, err := acc2.ExportAccountKeysForBackup() + assert.NoError(t, err) + assert.NotNil(t, sk2) + assert.NotNil(t, skProof2) + + g, _, err := protocoltypes.NewGroupMultiMember() + assert.NoError(t, err) + + omd1, err := acc1.GetOwnMemberDeviceForGroup(g) + assert.NoError(t, err) + + omd2, err := acc2.GetOwnMemberDeviceForGroup(g) + assert.NoError(t, err) + + omd1M := omd1.Member() + omd2M := omd2.Member() + omd1D := omd1.Device() + omd2D := omd2.Device() + + assert.True(t, omd1M.Equals(omd2M)) + assert.False(t, omd1D.Equals(omd2D)) +} diff --git a/pkg/secretstore/doc.go b/pkg/secretstore/doc.go new file mode 100644 index 00000000..470aac9b --- /dev/null +++ b/pkg/secretstore/doc.go @@ -0,0 +1,2 @@ +// Package secretstore contains function related to device, groups and messages keys. +package secretstore diff --git a/pkg/secretstore/keys_utils.go b/pkg/secretstore/keys_utils.go new file mode 100644 index 00000000..9c5c0aaa --- /dev/null +++ b/pkg/secretstore/keys_utils.go @@ -0,0 +1,147 @@ +package secretstore + +import ( + "crypto/ed25519" + "crypto/sha256" + "encoding/binary" + "fmt" + "io" + "io/ioutil" + + "github.com/libp2p/go-libp2p/core/crypto" + crypto_pb "github.com/libp2p/go-libp2p/core/crypto/pb" + "golang.org/x/crypto/hkdf" + "golang.org/x/crypto/sha3" + + "berty.tech/weshnet/pkg/cryptoutil" + "berty.tech/weshnet/pkg/errcode" + "berty.tech/weshnet/pkg/protocoltypes" +) + +// getEd25519PrivateKeyFromLibP2PFormattedBytes transforms an exported LibP2P +// private key into a crypto.PrivKey instance, ensuring it is an ed25519 key +func getEd25519PrivateKeyFromLibP2PFormattedBytes(rawKeyBytes []byte) (crypto.PrivKey, error) { + if len(rawKeyBytes) == 0 { + return nil, errcode.ErrInvalidInput.Wrap(fmt.Errorf("missing key data")) + } + + privateKey, err := crypto.UnmarshalPrivateKey(rawKeyBytes) + if err != nil { + return nil, errcode.ErrInvalidInput.Wrap(err) + } + + if privateKey.Type() != crypto_pb.KeyType_Ed25519 { + return nil, errcode.ErrInvalidInput.Wrap(fmt.Errorf("invalid key format")) + } + + return privateKey, nil +} + +// getKeysForGroupOfContact returns derived private keys for contact group +// using a private key via two accounts account keys (via an ECDH). +func getKeysForGroupOfContact(contactPairPrivateKey crypto.PrivKey) (crypto.PrivKey, crypto.PrivKey, error) { + // Salt length must be equal to hash length (64 bytes for sha256) + hash := sha256.New + + contactPairPrivateKeyBytes, err := contactPairPrivateKey.Raw() + if err != nil { + return nil, nil, errcode.ErrSerialization.Wrap(err) + } + + // Generate Pseudo Random Key using contactPairPrivateKeyBytes as IKM and salt + prk := hkdf.Extract(hash, contactPairPrivateKeyBytes, nil) + if len(prk) == 0 { + return nil, nil, errcode.ErrInternal.Wrap(fmt.Errorf("unable to instantiate pseudo random key")) + } + + // Expand using extracted prk and groupID as info (kind of namespace) + kdf := hkdf.Expand(hash, prk, nil) + + // Generate next KDF and message keys + groupSeed, err := ioutil.ReadAll(io.LimitReader(kdf, 32)) + if err != nil { + return nil, nil, errcode.ErrCryptoKeyGeneration.Wrap(err) + } + + groupSecretSeed, err := ioutil.ReadAll(io.LimitReader(kdf, 32)) + if err != nil { + return nil, nil, errcode.ErrCryptoKeyGeneration.Wrap(err) + } + + stdGroupPrivateKey := ed25519.NewKeyFromSeed(groupSeed) + groupPrivateKey, _, err := crypto.KeyPairFromStdKey(&stdGroupPrivateKey) + if err != nil { + return nil, nil, errcode.ErrCryptoKeyGeneration.Wrap(err) + } + + stdGroupSecretPrivateKey := ed25519.NewKeyFromSeed(groupSecretSeed) + groupSecretPrivateKey, _, err := crypto.KeyPairFromStdKey(&stdGroupSecretPrivateKey) + if err != nil { + return nil, nil, errcode.ErrCryptoKeyGeneration.Wrap(err) + } + + return groupPrivateKey, groupSecretPrivateKey, nil +} + +// getGroupForContact returns a protocoltypes.Group instance for a contact, +// using a private key via two accounts account keys (via an ECDH) +func getGroupForContact(contactPairPrivateKey crypto.PrivKey) (*protocoltypes.Group, error) { + groupPrivateKey, groupSecretPrivateKey, err := getKeysForGroupOfContact(contactPairPrivateKey) + if err != nil { + return nil, errcode.ErrCryptoKeyGeneration.Wrap(err) + } + + pubBytes, err := groupPrivateKey.GetPublic().Raw() + if err != nil { + return nil, errcode.ErrSerialization.Wrap(err) + } + + signingBytes, err := cryptoutil.SeedFromEd25519PrivateKey(groupSecretPrivateKey) + if err != nil { + return nil, errcode.ErrSerialization.Wrap(err) + } + + return &protocoltypes.Group{ + PublicKey: pubBytes, + Secret: signingBytes, + SecretSig: nil, + GroupType: protocoltypes.GroupTypeContact, + }, nil +} + +// getGroupOutOfStoreSecret retrieves the out of store group secret +func getGroupOutOfStoreSecret(m *protocoltypes.Group) ([]byte, error) { + if len(m.GetSecret()) == 0 { + return nil, errcode.ErrInvalidInput.Wrap(fmt.Errorf("no secret known for group")) + } + + arr := [cryptoutil.KeySize]byte{} + + kdf := hkdf.New(sha3.New256, m.GetSecret(), nil, []byte(namespaceOutOfStoreSecret)) + if _, err := io.ReadFull(kdf, arr[:]); err != nil { + return nil, errcode.ErrStreamRead.Wrap(err) + } + + return arr[:], nil +} + +// createOutOfStoreGroupReference creates a hash used to identify an out of +// store (e.g. push notification) message origin +func createOutOfStoreGroupReference(m *protocoltypes.Group, sender []byte, counter uint64) ([]byte, error) { + secret, err := getGroupOutOfStoreSecret(m) + if err != nil { + return nil, errcode.ErrInvalidInput.Wrap(err) + } + + arr := [cryptoutil.KeySize]byte{} + + buf := make([]byte, 8) + binary.BigEndian.PutUint64(buf, counter) + + kdf := hkdf.New(sha3.New256, secret, nil, append(sender, buf...)) + if _, err := io.ReadFull(kdf, arr[:]); err != nil { + return nil, errcode.ErrStreamRead.Wrap(err) + } + + return arr[:], nil +} diff --git a/pkg/secretstore/member_device.go b/pkg/secretstore/member_device.go new file mode 100644 index 00000000..daed5b9c --- /dev/null +++ b/pkg/secretstore/member_device.go @@ -0,0 +1,62 @@ +package secretstore + +import ( + "github.com/libp2p/go-libp2p/core/crypto" +) + +type ownMemberDevice struct { + member crypto.PrivKey + device crypto.PrivKey + public *memberDevice +} + +// newOwnMemberDevice instantiate a new ownMemberDevice allowing signing +// and encrypting data as both a device or a member part of a group. +// It also contains the public counterpart of the member and device keys. +func newOwnMemberDevice(member, device crypto.PrivKey) *ownMemberDevice { + return &ownMemberDevice{ + member: member, + device: device, + public: newMemberDevice(member.GetPublic(), device.GetPublic()), + } +} + +func (d *ownMemberDevice) MemberSign(data []byte) ([]byte, error) { + return d.member.Sign(data) +} + +func (d *ownMemberDevice) DeviceSign(data []byte) ([]byte, error) { + return d.device.Sign(data) +} + +func (d *ownMemberDevice) Member() crypto.PubKey { + return d.public.member +} + +func (d *ownMemberDevice) Device() crypto.PubKey { + return d.public.device +} + +type memberDevice struct { + member crypto.PubKey + device crypto.PubKey +} + +func NewMemberDevice(member, device crypto.PubKey) MemberDevice { + return newMemberDevice(member, device) +} + +func newMemberDevice(member, device crypto.PubKey) *memberDevice { + return &memberDevice{ + member: member, + device: device, + } +} + +func (m *memberDevice) Member() crypto.PubKey { + return m.member +} + +func (m *memberDevice) Device() crypto.PubKey { + return m.device +} diff --git a/pkg/secretstore/secret_store.go b/pkg/secretstore/secret_store.go new file mode 100644 index 00000000..3ae12961 --- /dev/null +++ b/pkg/secretstore/secret_store.go @@ -0,0 +1,379 @@ +package secretstore + +import ( + "context" + "fmt" + "sync" + + "github.com/ipfs/go-cid" + "github.com/ipfs/go-datastore" + dssync "github.com/ipfs/go-datastore/sync" + "github.com/libp2p/go-libp2p/core/crypto" + "go.uber.org/zap" + "golang.org/x/crypto/nacl/secretbox" + + "berty.tech/weshnet/internal/datastoreutil" + "berty.tech/weshnet/pkg/cryptoutil" + "berty.tech/weshnet/pkg/errcode" + "berty.tech/weshnet/pkg/ipfsutil" + "berty.tech/weshnet/pkg/protocoltypes" + "berty.tech/weshnet/pkg/pushtypes" +) + +const ( + namespaceDeviceKeystore = "device_keystore" + namespaceOutOfStoreSecret = "push_secret_ref" // nolint:gosec +) + +type secretStore struct { + logger *zap.Logger + datastore datastore.Datastore + deviceKeystore *deviceKeystore + + messageMutex sync.RWMutex + + preComputedKeysCount int + precomputeOutOfStoreGroupRefsCount uint64 +} + +func (o *NewSecretStoreOptions) applyDefaults(rootDatastore datastore.Datastore) { + if o.Logger == nil { + o.Logger = zap.NewNop() + } + + if o.Keystore == nil { + o.Keystore = ipfsutil.NewDatastoreKeystore(datastoreutil.NewNamespacedDatastore(rootDatastore, datastore.NewKey(namespaceDeviceKeystore))) + } + + if o.PreComputedKeysCount <= 0 { + o.PreComputedKeysCount = PrecomputeMessageKeyCount + } + + if o.PrecomputeOutOfStoreGroupRefsCount <= 0 { + o.PrecomputeOutOfStoreGroupRefsCount = PrecomputeOutOfStoreGroupRefsCount + } +} + +// NewSecretStore instantiates a new SecretStore +func NewSecretStore(rootDatastore datastore.Datastore, opts *NewSecretStoreOptions) (SecretStore, error) { + return newSecretStore(rootDatastore, opts) +} + +// newSecretStore instantiates a new secretStore +func newSecretStore(rootDatastore datastore.Datastore, opts *NewSecretStoreOptions) (*secretStore, error) { + if rootDatastore == nil { + return nil, errcode.ErrInvalidInput.Wrap(fmt.Errorf("a datastore is required")) + } + + if opts == nil { + opts = &NewSecretStoreOptions{} + } + + opts.applyDefaults(rootDatastore) + + devKeystore := newDeviceKeystore(opts.Keystore, opts.Logger) + + store := &secretStore{ + logger: opts.Logger, + datastore: rootDatastore, + deviceKeystore: devKeystore, + + preComputedKeysCount: opts.PreComputedKeysCount, + precomputeOutOfStoreGroupRefsCount: uint64(opts.PrecomputeOutOfStoreGroupRefsCount), + } + + return store, nil +} + +// NewInMemSecretStore instantiates a SecretStore using a volatile backend. +func NewInMemSecretStore(opts *NewSecretStoreOptions) (SecretStore, error) { + return newInMemSecretStore(opts) +} + +// newInMemSecretStore instantiates a secretStore using a volatile backend. +func newInMemSecretStore(opts *NewSecretStoreOptions) (*secretStore, error) { + return newSecretStore(dssync.MutexWrap(datastore.NewMapDatastore()), opts) +} + +func (s *secretStore) Close() error { + return nil +} + +func (s *secretStore) PutGroup(ctx context.Context, g *protocoltypes.Group) error { + pk, err := g.GetPubKey() + if err != nil { + return errcode.ErrInvalidInput.Wrap(err) + } + + // TODO: check if partial group or full group and complete if necessary + if ok, err := s.hasGroup(ctx, pk); err != nil { + return errcode.ErrInvalidInput.Wrap(err) + } else if ok { + return nil + } + + data, err := g.Marshal() + if err != nil { + return errcode.ErrSerialization.Wrap(err) + } + + if err := s.datastore.Put(ctx, dsKeyForGroup(g.GetPublicKey()), data); err != nil { + return errcode.ErrKeystorePut.Wrap(err) + } + + memberDevice, err := s.GetOwnMemberDeviceForGroup(g) + if err != nil { + return errcode.ErrInternal.Wrap(err) + } + + // Force generation of chain key for own device + _, err = s.GetShareableChainKey(ctx, g, memberDevice.Member()) + if err != nil { + return errcode.ErrInternal.Wrap(err) + } + + return nil +} + +func (s *secretStore) GetOwnMemberDeviceForGroup(g *protocoltypes.Group) (OwnMemberDevice, error) { + return s.deviceKeystore.memberDeviceForGroup(g) +} + +func (s *secretStore) OpenOutOfStoreMessage(ctx context.Context, payload []byte) (*protocoltypes.OutOfStoreMessage, *protocoltypes.Group, []byte, bool, error) { + oosMessageEnv := &pushtypes.OutOfStoreMessageEnvelope{} + if err := oosMessageEnv.Unmarshal(payload); err != nil { + return nil, nil, nil, false, errcode.ErrDeserialization.Wrap(err) + } + + groupPublicKey, err := s.OutOfStoreGetGroupPublicKeyByGroupReference(ctx, oosMessageEnv.GroupReference) + if err != nil { + return nil, nil, nil, false, errcode.ErrNotFound.Wrap(err) + } + + oosMessage, err := s.decryptOutOfStoreMessageEnv(ctx, oosMessageEnv, groupPublicKey) + if err != nil { + return nil, nil, nil, false, errcode.ErrCryptoDecrypt.Wrap(err) + } + + clear, newlyDecrypted, err := s.OutOfStoreMessageOpen(ctx, oosMessage, groupPublicKey) + if err != nil { + return nil, nil, nil, false, errcode.ErrCryptoDecrypt.Wrap(err) + } + + group, err := s.FetchGroupByPublicKey(ctx, groupPublicKey) + if err == nil { + if err := s.UpdateOutOfStoreGroupReferences(ctx, oosMessage.DevicePK, oosMessage.Counter, group); err != nil { + s.logger.Error("unable to update push group references", zap.Error(err)) + } + } + + return oosMessage, group, clear, !newlyDecrypted, nil +} + +func (s *secretStore) decryptOutOfStoreMessageEnv(ctx context.Context, env *pushtypes.OutOfStoreMessageEnvelope, groupPK crypto.PubKey) (*protocoltypes.OutOfStoreMessage, error) { + nonce, err := cryptoutil.NonceSliceToArray(env.Nonce) + if err != nil { + return nil, errcode.ErrInvalidInput.Wrap(err) + } + + g, err := s.FetchGroupByPublicKey(ctx, groupPK) + if err != nil { + return nil, errcode.ErrInvalidInput.Wrap(fmt.Errorf("unable to find group, err: %w", err)) + } + + secret := g.GetSharedSecret() + + data, ok := secretbox.Open(nil, env.Box, nonce, secret) + if !ok { + return nil, errcode.ErrCryptoDecrypt.Wrap(fmt.Errorf("unable to decrypt message")) + } + + outOfStoreMessage := &protocoltypes.OutOfStoreMessage{} + if err := outOfStoreMessage.Unmarshal(data); err != nil { + return nil, errcode.ErrDeserialization.Wrap(err) + } + + return outOfStoreMessage, nil +} + +func (s *secretStore) FetchGroupByPublicKey(ctx context.Context, publicKey crypto.PubKey) (*protocoltypes.Group, error) { + keyBytes, err := publicKey.Raw() + if err != nil { + return nil, errcode.ErrSerialization.Wrap(err) + } + + data, err := s.datastore.Get(ctx, dsKeyForGroup(keyBytes)) + if err != nil { + return nil, errcode.ErrMissingMapKey.Wrap(err) + } + + g := &protocoltypes.Group{} + if err := g.Unmarshal(data); err != nil { + return nil, errcode.ErrDeserialization.Wrap(err) + } + + return g, nil +} + +func (s *secretStore) GetAccountProofPublicKey() (crypto.PubKey, error) { + privateKey, err := s.deviceKeystore.getAccountPrivateKey() + if err != nil { + return nil, errcode.ErrInternal.Wrap(err) + } + + return privateKey.GetPublic(), nil +} + +func (s *secretStore) ImportAccountKeys(accountPrivateKeyBytes []byte, accountProofPrivateKeyBytes []byte) error { + return s.deviceKeystore.restoreAccountKeys(accountPrivateKeyBytes, accountProofPrivateKeyBytes) +} + +func (s *secretStore) ExportAccountKeysForBackup() (accountPrivateKeyBytes []byte, accountProofPrivateKeyBytes []byte, err error) { + accountPrivateKey, err := s.deviceKeystore.getAccountPrivateKey() + if err != nil { + return nil, nil, errcode.ErrInternal.Wrap(err) + } + + accountProofPrivateKey, err := s.deviceKeystore.getAccountProofPrivateKey() + if err != nil { + return nil, nil, errcode.ErrInternal.Wrap(err) + } + + accountPrivateKeyBytes, err = crypto.MarshalPrivateKey(accountPrivateKey) + if err != nil { + return nil, nil, errcode.ErrSerialization.Wrap(err) + } + + accountProofPrivateKeyBytes, err = crypto.MarshalPrivateKey(accountProofPrivateKey) + if err != nil { + return nil, nil, errcode.ErrSerialization.Wrap(err) + } + + return accountPrivateKeyBytes, accountProofPrivateKeyBytes, nil +} + +func (s *secretStore) GetAccountPrivateKey() (crypto.PrivKey, error) { + accountPrivateKey, err := s.deviceKeystore.getAccountPrivateKey() + if err != nil { + return nil, errcode.ErrInternal.Wrap(err) + } + + return accountPrivateKey, nil +} + +func (s *secretStore) GetGroupForAccount() (*protocoltypes.Group, OwnMemberDevice, error) { + accountPrivateKey, err := s.deviceKeystore.getAccountPrivateKey() + if err != nil { + return nil, nil, errcode.ErrOrbitDBOpen.Wrap(err) + } + + accountProofPrivateKey, err := s.deviceKeystore.getAccountProofPrivateKey() + if err != nil { + return nil, nil, errcode.ErrOrbitDBOpen.Wrap(err) + } + + devicePrivateKey, err := s.deviceKeystore.devicePrivateKey() + if err != nil { + return nil, nil, errcode.ErrInternal.Wrap(err) + } + + pubBytes, err := accountPrivateKey.GetPublic().Raw() + if err != nil { + return nil, nil, errcode.ErrSerialization.Wrap(err) + } + + signingBytes, err := cryptoutil.SeedFromEd25519PrivateKey(accountProofPrivateKey) + if err != nil { + return nil, nil, errcode.ErrSerialization.Wrap(err) + } + + return &protocoltypes.Group{ + PublicKey: pubBytes, + Secret: signingBytes, + SecretSig: nil, + GroupType: protocoltypes.GroupTypeAccount, + }, newOwnMemberDevice(accountPrivateKey, devicePrivateKey), nil +} + +func (s *secretStore) GetGroupForContact(contactPublicKey crypto.PubKey) (*protocoltypes.Group, error) { + contactPairPrivateKey, err := s.deviceKeystore.contactGroupPrivateKey(contactPublicKey) + if err != nil { + return nil, errcode.ErrCryptoKeyGeneration.Wrap(err) + } + + return getGroupForContact(contactPairPrivateKey) +} + +func (s *secretStore) OpenEnvelopeHeaders(data []byte, g *protocoltypes.Group) (*protocoltypes.MessageEnvelope, *protocoltypes.MessageHeaders, error) { + env := &protocoltypes.MessageEnvelope{} + err := env.Unmarshal(data) + if err != nil { + return nil, nil, errcode.ErrDeserialization.Wrap(err) + } + + nonce, err := cryptoutil.NonceSliceToArray(env.Nonce) + if err != nil { + return nil, nil, errcode.ErrSerialization.Wrap(fmt.Errorf("unable to convert slice to array: %w", err)) + } + + headersBytes, ok := secretbox.Open(nil, env.MessageHeaders, nonce, g.GetSharedSecret()) + if !ok { + return nil, nil, errcode.ErrCryptoDecrypt.Wrap(fmt.Errorf("secretbox failed to open headers")) + } + + headers := &protocoltypes.MessageHeaders{} + if err := headers.Unmarshal(headersBytes); err != nil { + return nil, nil, errcode.ErrDeserialization.Wrap(err) + } + + return env, headers, nil +} + +func (s *secretStore) SealOutOfStoreMessageEnvelope(id cid.Cid, env *protocoltypes.MessageEnvelope, headers *protocoltypes.MessageHeaders, g *protocoltypes.Group) (*pushtypes.OutOfStoreMessageEnvelope, error) { + oosMessage := &protocoltypes.OutOfStoreMessage{ + CID: id.Bytes(), + DevicePK: headers.DevicePK, + Counter: headers.Counter, + Sig: headers.Sig, + EncryptedPayload: env.Message, + Nonce: env.Nonce, + } + + data, err := oosMessage.Marshal() + if err != nil { + return nil, errcode.ErrSerialization.Wrap(err) + } + + nonce, err := cryptoutil.GenerateNonce() + if err != nil { + return nil, errcode.ErrCryptoNonceGeneration.Wrap(err) + } + + secret, err := cryptoutil.KeySliceToArray(g.Secret) + if err != nil { + return nil, errcode.ErrCryptoKeyConversion.Wrap(fmt.Errorf("unable to convert slice to array: %w", err)) + } + + encryptedData := secretbox.Seal(nil, data, nonce, secret) + + pushGroupRef, err := createOutOfStoreGroupReference(g, headers.DevicePK, headers.Counter) + if err != nil { + return nil, errcode.ErrCryptoKeyGeneration.Wrap(err) + } + + return &pushtypes.OutOfStoreMessageEnvelope{ + Nonce: nonce[:], + Box: encryptedData, + GroupReference: pushGroupRef, + }, nil +} + +// hasGroup checks whether a group is already known by the secretStore +func (s *secretStore) hasGroup(ctx context.Context, key crypto.PubKey) (bool, error) { + keyBytes, err := key.Raw() + if err != nil { + return false, errcode.ErrSerialization.Wrap(err) + } + + return s.datastore.Has(ctx, dsKeyForGroup(keyBytes)) +} diff --git a/pkg/secretstore/secret_store_interfaces.go b/pkg/secretstore/secret_store_interfaces.go new file mode 100644 index 00000000..5291db7f --- /dev/null +++ b/pkg/secretstore/secret_store_interfaces.go @@ -0,0 +1,152 @@ +package secretstore + +import ( + "context" + + "github.com/ipfs/go-cid" + keystore "github.com/ipfs/go-ipfs-keystore" + "github.com/libp2p/go-libp2p/core/crypto" + "go.uber.org/zap" + + "berty.tech/weshnet/pkg/cryptoutil" + "berty.tech/weshnet/pkg/protocoltypes" + "berty.tech/weshnet/pkg/pushtypes" +) + +const ( + PrecomputeOutOfStoreGroupRefsCount = 100 + PrecomputeMessageKeyCount = 100 +) + +type messageKey [32]byte + +type SecretStore interface { + // + // Account methods + // + + // GetAccountProofPublicKey returns the user's account proof public key + GetAccountProofPublicKey() (accountProofPublicKey crypto.PubKey, err error) + + // ImportAccountKeys restores backup of account keys into the SecretStore, it should fail if the store is already used by an account + ImportAccountKeys(accountPrivateKey []byte, accountProofPrivateKey []byte) error + + // ExportAccountKeysForBackup returns the account's private key and proof private key of the user for a backup + ExportAccountKeysForBackup() (accountPrivateKey []byte, accountProofPrivateKey []byte, err error) + + // GetAccountPrivateKey returns the account's private key, avoid using it, use GetGroupForAccount to get the account public key or sign data instead + GetAccountPrivateKey() (accountPrivateKey crypto.PrivKey, err error) + + // + // Groups methods + // + + // GetGroupForAccount returns the Account's Group of the user + GetGroupForAccount() (group *protocoltypes.Group, ownMemberDevice OwnMemberDevice, err error) + + // GetGroupForContact returns a contact group for communicating with the provided account + GetGroupForContact(contactPublicKey crypto.PubKey) (group *protocoltypes.Group, err error) + + // PutGroup stores a group into the store + PutGroup(ctx context.Context, group *protocoltypes.Group) error + + // FetchGroupByPublicKey gets an account from the store using the provided public key + FetchGroupByPublicKey(ctx context.Context, publicKey crypto.PubKey) (group *protocoltypes.Group, err error) + + // + // Envelopes methods + // + + // OpenEnvelopeHeaders opens a message headers for a given group + OpenEnvelopeHeaders(data []byte, group *protocoltypes.Group) (*protocoltypes.MessageEnvelope, *protocoltypes.MessageHeaders, error) + + // OpenEnvelopePayload opens a message payload with the given group headers + OpenEnvelopePayload(ctx context.Context, msgEnvelope *protocoltypes.MessageEnvelope, msgHeaders *protocoltypes.MessageHeaders, groupPublicKey crypto.PubKey, ownPublicKey crypto.PubKey, msgCID cid.Cid) (*protocoltypes.EncryptedMessage, error) + + // SealEnvelope creates an encrypted payload to be sent to a group + SealEnvelope(ctx context.Context, group *protocoltypes.Group, messagePayload []byte) (sealedEnvelope []byte, err error) + + // + // Group member-device pairs methods + // + + // GetOwnMemberDeviceForGroup gets a member and device key-pairs representing the current device in a given group + GetOwnMemberDeviceForGroup(group *protocoltypes.Group) (OwnMemberDevice, error) + + // + // Chain-keys methods + // + + // RegisterChainKey records another device chain-key + RegisterChainKey(ctx context.Context, group *protocoltypes.Group, senderDevicePublicKey crypto.PubKey, encryptedDeviceChainKey []byte) error + + // GetShareableChainKey returns a chain-key that can be decrypted by the provided member of a group + GetShareableChainKey(ctx context.Context, group *protocoltypes.Group, targetMemberPublicKey crypto.PubKey) (encryptedDeviceChainKey []byte, err error) + + // IsChainKeyKnownForDevice checks whether a chain key of a device is already known + IsChainKeyKnownForDevice(ctx context.Context, groupPublicKey crypto.PubKey, devicePublicKey crypto.PubKey) (isKnown bool) + + // + // Out-of-store messages methods + // + + // SealOutOfStoreMessageEnvelope encrypts a message to be sent outside a synchronized store + SealOutOfStoreMessageEnvelope(id cid.Cid, env *protocoltypes.MessageEnvelope, headers *protocoltypes.MessageHeaders, group *protocoltypes.Group) (*pushtypes.OutOfStoreMessageEnvelope, error) + + // OpenOutOfStoreMessage opens a message received outside a synchronized store + OpenOutOfStoreMessage(ctx context.Context, payload []byte) (outOfStoreMessage *protocoltypes.OutOfStoreMessage, group *protocoltypes.Group, clearPayload []byte, alreadyDecrypted bool, err error) + + // UpdateOutOfStoreGroupReferences computes references of messages which might be received outside a synchronized store + UpdateOutOfStoreGroupReferences(ctx context.Context, devicePublicKeyBytes []byte, first uint64, group *protocoltypes.Group) error + + // Close frees resources created by the secret store + Close() error +} + +// NewSecretStoreOptions contains the options that can be passed to NewSecretStore +type NewSecretStoreOptions struct { + // PreComputedKeysCount specifies the number of keys to precompute, + // defaults to PrecomputeMessageKeyCount + PreComputedKeysCount int + + // PreComputedKeysCount specifies the number of out of store references + // to precompute, defaults to PrecomputeOutOfStoreGroupRefsCount + PrecomputeOutOfStoreGroupRefsCount int + + // Keystore specifies an implementation of a keystore to be used, can be + // helpful if you want to rely on a hardware based keystore instead of a + // software one + Keystore keystore.Keystore + + // Logger specifies which logger to use, logging is disabled by default + Logger *zap.Logger + + // DisableOutOfStoreSupport explicitly disables support of out-of-store + // payloads + DisableOutOfStoreSupport bool + + // OutOfStorePrivateKey sets the out-of-store key to decrypt payloads + // received via a server, alternatively you can use + // SecretStore.SetOutOfStoreDevicePrivateKey to set it + OutOfStorePrivateKey *[cryptoutil.KeySize]byte +} + +// MemberDevice is the public keys of a device and its member +type MemberDevice interface { + // Member returns the member public key + Member() crypto.PubKey + + // Device returns the device public key + Device() crypto.PubKey +} + +// OwnMemberDevice is a MemberDevice for the current device, able to sign data +type OwnMemberDevice interface { + MemberDevice + + // MemberSign signs the given data as a member of a group + MemberSign(data []byte) ([]byte, error) + + // DeviceSign signs the given data as a device of a group + DeviceSign(data []byte) ([]byte, error) +} diff --git a/pkg/secretstore/secret_store_messages.go b/pkg/secretstore/secret_store_messages.go new file mode 100644 index 00000000..d880be4a --- /dev/null +++ b/pkg/secretstore/secret_store_messages.go @@ -0,0 +1,1067 @@ +package secretstore + +import ( + "context" + "crypto/sha256" + "encoding/binary" + "fmt" + "io" + "io/ioutil" + + "github.com/gogo/protobuf/proto" + "github.com/ipfs/go-cid" + "github.com/ipfs/go-datastore" + "github.com/libp2p/go-libp2p/core/crypto" + "go.uber.org/zap" + "golang.org/x/crypto/hkdf" + "golang.org/x/crypto/nacl/secretbox" + + "berty.tech/weshnet/pkg/cryptoutil" + "berty.tech/weshnet/pkg/errcode" + "berty.tech/weshnet/pkg/logutil" + "berty.tech/weshnet/pkg/protocoltypes" +) + +// decryptionContext contains context about a decrypted message, its CID, the +// associated message key and whether it had been previously opened. +type decryptionContext struct { + newlyDecrypted bool + messageKey *messageKey + cid cid.Cid +} + +// computedMessageKey is a precomputed message key for a given counter used in the cache namespace. +type computedMessageKey struct { + counter uint64 + messageKey *messageKey +} + +// getDeviceChainKeyForGroupAndDevice returns the device chain key for the given group and device. +func (s *secretStore) getDeviceChainKeyForGroupAndDevice(ctx context.Context, groupPublicKey crypto.PubKey, devicePublicKey crypto.PubKey) (*protocoltypes.DeviceChainKey, error) { + if s == nil { + return nil, errcode.ErrInvalidInput.Wrap(fmt.Errorf("calling method of a non instantiated message keystore")) + } + + key, err := dsKeyForCurrentChainKey(groupPublicKey, devicePublicKey) + if err != nil { + return nil, errcode.ErrInternal.Wrap(err) + } + + // Not mutex here + dsBytes, err := s.datastore.Get(ctx, key) + + if err == datastore.ErrNotFound { + return nil, errcode.ErrMissingInput.Wrap(err) + } else if err != nil { + return nil, errcode.ErrMessageKeyPersistenceGet.Wrap(err) + } + + ds := &protocoltypes.DeviceChainKey{} + if err := ds.Unmarshal(dsBytes); err != nil { + return nil, errcode.ErrInvalidInput.Wrap(err) + } + + return ds, nil +} + +// IsChainKeyKnownForDevice returns true if the device chain key is known for the given group and device. +func (s *secretStore) IsChainKeyKnownForDevice(ctx context.Context, groupPublicKey crypto.PubKey, devicePublicKey crypto.PubKey) (has bool) { + if s == nil { + return false + } + + key, err := dsKeyForCurrentChainKey(groupPublicKey, devicePublicKey) + if err != nil { + return false + } + + s.messageMutex.RLock() + defer s.messageMutex.RUnlock() + + has, _ = s.datastore.Has(ctx, key) + + return +} + +// delPrecomputedKey deletes the message key in the cache namespace for the given group, device and counter. +func (s *secretStore) delPrecomputedKey(ctx context.Context, groupPublicKey crypto.PubKey, devicePublicKey crypto.PubKey, msgCounter uint64) error { + if s == nil { + return errcode.ErrInvalidInput.Wrap(fmt.Errorf("calling method of a non instantiated message keystore")) + } + + devicePublicKeyRaw, err := devicePublicKey.Raw() + if err != nil { + return errcode.ErrSerialization.Wrap(err) + } + + groupPublicKeyRaw, err := groupPublicKey.Raw() + if err != nil { + return errcode.ErrSerialization.Wrap(err) + } + + id := dsKeyForPrecomputedMessageKey(groupPublicKeyRaw, devicePublicKeyRaw, msgCounter) + + err = s.datastore.Delete(ctx, id) + + if err != nil { + return errcode.ErrMessageKeyPersistencePut.Wrap(err) + } + + return nil +} + +// postDecryptActions is called after a message has been decrypted. +// It saves the message key from the cache namespace to find it quickly on subsequent read operations. +// It derives the chain key in the cache namespace. +func (s *secretStore) postDecryptActions(ctx context.Context, decryptionCtx *decryptionContext, groupPublicKey crypto.PubKey, ownPublicKey crypto.PubKey, msgHeaders *protocoltypes.MessageHeaders) error { + if s == nil { + return errcode.ErrInvalidInput.Wrap(fmt.Errorf("calling method of a non instantiated message keystore")) + } + + // Message was newly decrypted, we can save the message key and derive + // future keys if necessary. + if decryptionCtx == nil || !decryptionCtx.newlyDecrypted { + return nil + } + + var ( + deviceChainKey *protocoltypes.DeviceChainKey + err error + ) + + devicePublicKey, err := crypto.UnmarshalEd25519PublicKey(msgHeaders.DevicePK) + if err != nil { + return errcode.ErrDeserialization.Wrap(err) + } + + if err = s.putKeyForCID(ctx, decryptionCtx.cid, decryptionCtx.messageKey); err != nil { + return errcode.ErrInternal.Wrap(err) + } + + if err = s.delPrecomputedKey(ctx, groupPublicKey, devicePublicKey, msgHeaders.Counter); err != nil { + return errcode.ErrInternal.Wrap(err) + } + + if deviceChainKey, err = s.preComputeNextKey(ctx, groupPublicKey, devicePublicKey); err != nil { + return errcode.ErrInternal.Wrap(err) + } + + // If the message was not emitted by the current Device we might need + // to update the current chain key + if ownPublicKey == nil || !ownPublicKey.Equals(devicePublicKey) { + if err = s.updateCurrentKey(ctx, groupPublicKey, devicePublicKey, deviceChainKey); err != nil { + return errcode.ErrInternal.Wrap(err) + } + } + + return nil +} + +func (s *secretStore) GetShareableChainKey(ctx context.Context, group *protocoltypes.Group, targetMemberPublicKey crypto.PubKey) ([]byte, error) { + deviceChainKey, err := s.getOwnDeviceChainKeyForGroup(ctx, group) + if err != nil { + return nil, errcode.ErrInvalidInput.Wrap(err) + } + + privateMemberDevice, err := s.deviceKeystore.memberDeviceForGroup(group) + if err != nil { + return nil, errcode.ErrInvalidInput.Wrap(err) + } + + encryptedDeviceChainKey, err := encryptDeviceChainKey(privateMemberDevice.device, targetMemberPublicKey, deviceChainKey, group) + if err != nil { + return nil, errcode.ErrCryptoEncrypt.Wrap(err) + } + + return encryptedDeviceChainKey, nil +} + +// getOwnDeviceChainKeyForGroup returns the device chain key for the current +// device on a given group. +// If the chain key has not been created yet, it will be generated and +// registered. +func (s *secretStore) getOwnDeviceChainKeyForGroup(ctx context.Context, group *protocoltypes.Group) (*protocoltypes.DeviceChainKey, error) { + if s == nil { + return nil, errcode.ErrInvalidInput.Wrap(fmt.Errorf("calling method of a non instantiated message keystore")) + } + + if s.deviceKeystore == nil { + return nil, errcode.ErrCryptoSignature.Wrap(fmt.Errorf("message keystore is opened in read-only mode")) + } + + md, err := s.deviceKeystore.memberDeviceForGroup(group) + if err != nil { + return nil, errcode.ErrInternal.Wrap(err) + } + + groupPublicKey, err := group.GetPubKey() + if err != nil { + return nil, errcode.ErrDeserialization.Wrap(err) + } + + s.messageMutex.Lock() + defer s.messageMutex.Unlock() + + ds, err := s.getDeviceChainKeyForGroupAndDevice(ctx, groupPublicKey, md.Device()) + if errcode.Is(err, errcode.ErrMissingInput) { + // If secret does not exist, create it + deviceChainKey, err := newDeviceChainKey() + if err != nil { + return nil, errcode.ErrCryptoKeyGeneration.Wrap(err) + } + + if err = s.registerChainKey(ctx, group, md.Device(), deviceChainKey, true); err != nil { + return nil, errcode.ErrMessageKeyPersistencePut.Wrap(err) + } + + return deviceChainKey, nil + } + if err != nil { + return nil, errcode.ErrMessageKeyPersistenceGet.Wrap(err) + } + + return ds, nil +} + +// RegisterChainKey registers a chain key for the given group and device. +// If the device chain key is not from the current device, the function will +// precompute and store in the cache namespace the next message keys. +// It is the exported version of registerChainKey. +func (s *secretStore) RegisterChainKey(ctx context.Context, group *protocoltypes.Group, senderDevicePublicKey crypto.PubKey, encryptedDeviceChainKey []byte) error { + if s == nil { + return errcode.ErrInvalidInput.Wrap(fmt.Errorf("calling method of a non instantiated message keystore")) + } + + if s.deviceKeystore == nil { + return errcode.ErrCryptoSignature.Wrap(fmt.Errorf("message keystore is opened in read-only mode")) + } + + localMemberDevice, err := s.deviceKeystore.memberDeviceForGroup(group) + if err != nil { + return errcode.ErrGroupMemberUnknownGroupID.Wrap(err) + } + + deviceChainKey, err := decryptDeviceChainKey(encryptedDeviceChainKey, group, localMemberDevice.member, senderDevicePublicKey) + if err != nil { + return errcode.ErrCryptoDecrypt.Wrap(err) + } + + hasSecretBeenSentByCurrentDevice := localMemberDevice.Member().Equals(senderDevicePublicKey) + + return s.registerChainKey(ctx, group, senderDevicePublicKey, deviceChainKey, hasSecretBeenSentByCurrentDevice) +} + +// registerChainKey registers a chain key for the given group and device. +// If the chain key is not from the current device, the function will +// precompute and store in the cache namespace the next message keys. +func (s *secretStore) registerChainKey(ctx context.Context, group *protocoltypes.Group, devicePublicKey crypto.PubKey, deviceChainKey *protocoltypes.DeviceChainKey, isCurrentDeviceChainKey bool) error { + if s == nil { + return errcode.ErrInvalidInput.Wrap(fmt.Errorf("calling method of a non instantiated message keystore")) + } + + groupPublicKey, err := group.GetPubKey() + if err != nil { + return errcode.ErrDeserialization.Wrap(err) + } + + if _, err := s.getDeviceChainKeyForGroupAndDevice(ctx, groupPublicKey, devicePublicKey); err == nil { + // Device is already registered, ignore it + s.logger.Debug("device already registered in group", + logutil.PrivateBinary("devicePublicKey", logutil.CryptoKeyToBytes(devicePublicKey)), + logutil.PrivateBinary("groupPublicKey", logutil.CryptoKeyToBytes(groupPublicKey)), + ) + return nil + } + + s.logger.Debug("registering chain key", + logutil.PrivateBinary("devicePublicKey", logutil.CryptoKeyToBytes(devicePublicKey)), + logutil.PrivateBinary("groupPublicKey", logutil.CryptoKeyToBytes(groupPublicKey)), + ) + + // If own Device store key as is, no need to precompute future keys + if isCurrentDeviceChainKey { + if err := s.putDeviceChainKey(ctx, groupPublicKey, devicePublicKey, deviceChainKey); err != nil { + return errcode.ErrInternal.Wrap(err) + } + + return nil + } + + s.messageMutex.Lock() + + if deviceChainKey, err = s.preComputeKeys(ctx, devicePublicKey, groupPublicKey, deviceChainKey); err != nil { + s.messageMutex.Unlock() + return errcode.ErrCryptoKeyGeneration.Wrap(err) + } + + if err := s.putDeviceChainKey(ctx, groupPublicKey, devicePublicKey, deviceChainKey); err != nil { + s.messageMutex.Unlock() + return errcode.ErrInternal.Wrap(err) + } + + s.messageMutex.Unlock() + + devicePublicKeyBytes, err := devicePublicKey.Raw() + if err == nil { + if err := s.UpdateOutOfStoreGroupReferences(ctx, devicePublicKeyBytes, deviceChainKey.Counter, group); err != nil { + s.logger.Error("updating out of store group references failed", zap.Error(err)) + } + } + + return nil +} + +// preComputeKeys precomputes the next m.preComputedKeysCount keys for the given device and group and put them in the cache namespace. +func (s *secretStore) preComputeKeys(ctx context.Context, devicePublicKey crypto.PubKey, groupPublicKey crypto.PubKey, deviceChainKey *protocoltypes.DeviceChainKey) (*protocoltypes.DeviceChainKey, error) { + if s == nil { + return nil, errcode.ErrInvalidInput.Wrap(fmt.Errorf("calling method of a non instantiated message keystore")) + } + + chainKeyValue := deviceChainKey.ChainKey + counter := deviceChainKey.Counter + + groupPublicKeyBytes, err := groupPublicKey.Raw() + if err != nil { + return nil, errcode.ErrSerialization.Wrap(err) + } + + knownDeviceChainKey, err := s.getDeviceChainKeyForGroupAndDevice(ctx, groupPublicKey, devicePublicKey) + if err != nil && !errcode.Is(err, errcode.ErrMissingInput) { + return nil, errcode.ErrInternal.Wrap(err) + } + + var preComputedKeys []computedMessageKey + for i := 0; i < s.getPrecomputedKeyExpectedCount(); i++ { + counter++ + + knownMK, err := s.getPrecomputedMessageKey(ctx, groupPublicKey, devicePublicKey, counter) + if err != nil && !errcode.Is(err, errcode.ErrMissingInput) { + return nil, errcode.ErrInternal.Wrap(err) + } + + newChainKeyValue, mk, err := deriveNextKeys(chainKeyValue, nil, groupPublicKeyBytes) + if err != nil { + return nil, errcode.TODO.Wrap(err) + } + + chainKeyValue = newChainKeyValue + + if knownMK != nil && knownDeviceChainKey != nil { + if knownDeviceChainKey.Counter != counter-1 { + continue + } + } + + preComputedKeys = append(preComputedKeys, computedMessageKey{counter, &mk}) + } + + err = s.putPrecomputedKeys(ctx, groupPublicKey, devicePublicKey, preComputedKeys) + if err != nil { + return nil, errcode.TODO.Wrap(err) + } + + return &protocoltypes.DeviceChainKey{ + Counter: counter, + ChainKey: chainKeyValue, + }, nil +} + +// getPrecomputedKeyExpectedCount returns the number of precomputed keys that +// should be in the cache namespace of the keystore. +func (s *secretStore) getPrecomputedKeyExpectedCount() int { + if s == nil || s.preComputedKeysCount < 0 { + return 0 + } + + return s.preComputedKeysCount +} + +// preComputeNextKey precomputes the next key for the given group and device and adds it to the cache namespace. +func (s *secretStore) preComputeNextKey(ctx context.Context, groupPublicKey crypto.PubKey, devicePublicKey crypto.PubKey) (*protocoltypes.DeviceChainKey, error) { + if s == nil { + return nil, errcode.ErrInvalidInput.Wrap(fmt.Errorf("calling method of a non instantiated message keystore")) + } + + if devicePublicKey == nil { + return nil, errcode.ErrInvalidInput.Wrap(fmt.Errorf("devicePublicKey cannot be nil")) + } + + groupPublicKeyBytes, err := groupPublicKey.Raw() + if err != nil { + return nil, errcode.ErrSerialization.Wrap(err) + } + + ds, err := s.getDeviceChainKeyForGroupAndDevice(ctx, groupPublicKey, devicePublicKey) + if err != nil { + return nil, errcode.ErrInternal.Wrap(err) + } + + newCounter := ds.Counter + 1 + + // TODO: Salt? + newCK, mk, err := deriveNextKeys(ds.ChainKey, nil, groupPublicKeyBytes) + if err != nil { + return nil, errcode.TODO.Wrap(err) + } + + err = s.putPrecomputedKeys(ctx, groupPublicKey, devicePublicKey, []computedMessageKey{{newCounter, &mk}}) + if err != nil { + return nil, errcode.TODO.Wrap(err) + } + + return &protocoltypes.DeviceChainKey{ + Counter: newCounter, + ChainKey: newCK, + }, nil +} + +// getPrecomputedMessageKey returns the precomputed message key put in the cache +// namespace for the given group and device at the given counter. +func (s *secretStore) getPrecomputedMessageKey(ctx context.Context, groupPublicKey crypto.PubKey, devicePublicKey crypto.PubKey, counter uint64) (*messageKey, error) { + if s == nil { + return nil, errcode.ErrInvalidInput.Wrap(fmt.Errorf("calling method of a non instantiated message keystore")) + } + + deviceRaw, err := devicePublicKey.Raw() + if err != nil { + return nil, errcode.ErrSerialization.Wrap(err) + } + + groupRaw, err := groupPublicKey.Raw() + if err != nil { + return nil, errcode.ErrSerialization.Wrap(err) + } + + id := dsKeyForPrecomputedMessageKey(groupRaw, deviceRaw, counter) + + key, err := s.datastore.Get(ctx, id) + + if err == datastore.ErrNotFound { + return nil, errcode.ErrMissingInput.Wrap(fmt.Errorf("key for message does not exist in datastore")) + } + if err != nil { + return nil, errcode.ErrMessageKeyPersistenceGet.Wrap(err) + } + + keyArray, err := cryptoutil.KeySliceToArray(key) + if err != nil { + return nil, errcode.ErrSerialization.Wrap(err) + } + + return (*messageKey)(keyArray), nil +} + +// putPrecomputedKeys puts the given precomputed keys in the cache namespace. +// It will try to use a batch if the store supports it. +func (s *secretStore) putPrecomputedKeys(ctx context.Context, groupPublicKey crypto.PubKey, devicePublicKey crypto.PubKey, preComputedMessageKeys []computedMessageKey) error { + if s == nil { + return errcode.ErrInvalidInput.Wrap(fmt.Errorf("calling method of a non instantiated message keystore")) + } + + s.logger.Debug("putting precomputed keys", zap.Int("count", len(preComputedMessageKeys))) + + if len(preComputedMessageKeys) == 0 { + return nil + } + + deviceRaw, err := devicePublicKey.Raw() + if err != nil { + return errcode.ErrSerialization.Wrap(err) + } + + groupRaw, err := groupPublicKey.Raw() + if err != nil { + return errcode.ErrSerialization.Wrap(err) + } + + if batchedDatastore, ok := s.datastore.(datastore.BatchingFeature); ok { + batch, err := batchedDatastore.Batch(ctx) + if err == datastore.ErrBatchUnsupported { + return s.putPrecomputedKeysNonBatched(ctx, groupRaw, deviceRaw, preComputedMessageKeys) + } + + return s.putPrecomputedKeysBatched(ctx, batch, groupRaw, deviceRaw, preComputedMessageKeys) + } + + return s.putPrecomputedKeysNonBatched(ctx, groupRaw, deviceRaw, preComputedMessageKeys) +} + +func (s *secretStore) putPrecomputedKeysBatched(ctx context.Context, batch datastore.Batch, groupRaw []byte, deviceRaw []byte, preComputedMessageKeys []computedMessageKey) error { + for _, preComputedKey := range preComputedMessageKeys { + id := dsKeyForPrecomputedMessageKey(groupRaw, deviceRaw, preComputedKey.counter) + + if err := batch.Put(ctx, id, preComputedKey.messageKey[:]); err != nil { + return errcode.ErrMessageKeyPersistencePut.Wrap(err) + } + } + + if err := batch.Commit(ctx); err != nil { + return errcode.ErrMessageKeyPersistencePut.Wrap(err) + } + + return nil +} + +func (s *secretStore) putPrecomputedKeysNonBatched(ctx context.Context, groupRaw []byte, deviceRaw []byte, preComputedMessageKeys []computedMessageKey) error { + for _, preComputedKey := range preComputedMessageKeys { + fmt.Println(">", groupRaw, deviceRaw, preComputedKey.counter) + id := dsKeyForPrecomputedMessageKey(groupRaw, deviceRaw, preComputedKey.counter) + + if err := s.datastore.Put(ctx, id, preComputedKey.messageKey[:]); err != nil { + return errcode.ErrMessageKeyPersistencePut.Wrap(err) + } + } + + return nil +} + +// putKeyForCID puts the given message key in the datastore for a specified CID. +func (s *secretStore) putKeyForCID(ctx context.Context, messageCID cid.Cid, messageKey *messageKey) error { + if s == nil { + return errcode.ErrInvalidInput.Wrap(fmt.Errorf("calling method of a non instantiated message keystore")) + } + + if !messageCID.Defined() { + return nil + } + + err := s.datastore.Put(ctx, dsKeyForMessageKeyByCID(messageCID), messageKey[:]) + if err != nil { + return errcode.ErrMessageKeyPersistencePut.Wrap(err) + } + + return nil +} + +// OpenEnvelopePayload opens the payload of a message envelope and returns the +// decrypted message in its EncryptedMessage form. +// It also performs post decryption actions such as updating message key cache. +func (s *secretStore) OpenEnvelopePayload(ctx context.Context, msgEnvelope *protocoltypes.MessageEnvelope, msgHeaders *protocoltypes.MessageHeaders, groupPublicKey crypto.PubKey, ownDevicePublicKey crypto.PubKey, msgCID cid.Cid) (*protocoltypes.EncryptedMessage, error) { + s.messageMutex.Lock() + defer s.messageMutex.Unlock() + + msgBytes, decryptionCtx, err := s.openPayload(ctx, msgCID, groupPublicKey, msgEnvelope.Message, msgHeaders) + if err != nil { + return nil, errcode.ErrCryptoDecryptPayload.Wrap(err) + } + + if err := s.postDecryptActions(ctx, decryptionCtx, groupPublicKey, ownDevicePublicKey, msgHeaders); err != nil { + return nil, errcode.TODO.Wrap(err) + } + + var msg protocoltypes.EncryptedMessage + err = msg.Unmarshal(msgBytes) + if err != nil { + return nil, errcode.ErrDeserialization.Wrap(err) + } + + return &msg, nil +} + +// openPayload opens the payload of a message envelope and returns the +// decrypted message. +// It retrieves the message key from the keystore or the cache to decrypt +// the message. +func (s *secretStore) openPayload(ctx context.Context, msgCID cid.Cid, groupPublicKey crypto.PubKey, payload []byte, msgHeaders *protocoltypes.MessageHeaders) ([]byte, *decryptionContext, error) { + if s == nil { + return nil, nil, errcode.ErrInvalidInput.Wrap(fmt.Errorf("calling method of a non instantiated message keystore")) + } + + var ( + err error + decryptionCtx = &decryptionContext{ + cid: msgCID, + newlyDecrypted: true, + } + publicKey crypto.PubKey + ) + + if decryptionCtx.messageKey, err = s.getKeyForCID(ctx, msgCID); err == nil { + decryptionCtx.newlyDecrypted = false + } else { + publicKey, err = crypto.UnmarshalEd25519PublicKey(msgHeaders.DevicePK) + if err != nil { + return nil, nil, errcode.ErrDeserialization.Wrap(err) + } + + decryptionCtx.messageKey, err = s.getPrecomputedMessageKey(ctx, groupPublicKey, publicKey, msgHeaders.Counter) + if err != nil { + return nil, nil, errcode.ErrCryptoDecrypt.Wrap(err) + } + } + + return s.openPayloadWithMessageKey(decryptionCtx, publicKey, payload, msgHeaders) +} + +// openPayloadWithMessageKey opens the payload of a message envelope with the +// given key and returns the decrypted message with the decryptionContext +// struct. +func (s *secretStore) openPayloadWithMessageKey(decryptionCtx *decryptionContext, devicePublicKey crypto.PubKey, payload []byte, headers *protocoltypes.MessageHeaders) ([]byte, *decryptionContext, error) { + msg, ok := secretbox.Open(nil, payload, uint64AsNonce(headers.Counter), (*[32]byte)(decryptionCtx.messageKey)) + if !ok { + return nil, nil, errcode.ErrCryptoDecrypt.Wrap(fmt.Errorf("secret box failed to open message payload")) + } + + if decryptionCtx.newlyDecrypted { + if ok, err := devicePublicKey.Verify(msg, headers.Sig); !ok { + return nil, nil, errcode.ErrCryptoSignatureVerification.Wrap(fmt.Errorf("unable to verify message signature")) + } else if err != nil { + return nil, nil, errcode.ErrCryptoSignatureVerification.Wrap(err) + } + } + + // Message was newly decrypted, we can save the message key and derive + // future keys if necessary. + return msg, decryptionCtx, nil +} + +// getKeyForCID retrieves the message key for the given message CID. +func (s *secretStore) getKeyForCID(ctx context.Context, msgCID cid.Cid) (*messageKey, error) { + if s == nil { + return nil, errcode.ErrInvalidInput.Wrap(fmt.Errorf("calling method of a non instantiated message keystore")) + } + + if !msgCID.Defined() { + return nil, errcode.ErrInvalidInput.Wrap(fmt.Errorf("undefined message CID")) + } + + msgKey, err := s.datastore.Get(ctx, dsKeyForMessageKeyByCID(msgCID)) + + if err == datastore.ErrNotFound { + return nil, errcode.ErrInvalidInput.Wrap(err) + } + + msgKeyArray, err := cryptoutil.KeySliceToArray(msgKey) + if err != nil { + return nil, errcode.ErrSerialization.Wrap(err) + } + + return (*messageKey)(msgKeyArray), nil +} + +// putDeviceChainKey stores the chain key for the given group and device. +func (s *secretStore) putDeviceChainKey(ctx context.Context, groupPublicKey crypto.PubKey, devicePublicKey crypto.PubKey, deviceChainKey *protocoltypes.DeviceChainKey) error { + if s == nil { + return errcode.ErrInvalidInput.Wrap(fmt.Errorf("calling method of a non instantiated message keystore")) + } + + deviceChainKeyBytes, err := deviceChainKey.Marshal() + if err != nil { + return errcode.ErrSerialization.Wrap(err) + } + + datastoreKey, err := dsKeyForCurrentChainKey(groupPublicKey, devicePublicKey) + if err != nil { + return errcode.ErrSerialization.Wrap(err) + } + + err = s.datastore.Put(ctx, datastoreKey, deviceChainKeyBytes) + if err != nil { + return errcode.ErrMessageKeyPersistencePut.Wrap(err) + } + + return nil +} + +// SealEnvelope encrypts the given payload and returns it as an envelope to be +// published on the group's store. +// It retrieves the device's chain key from the keystore to encrypt the payload +// using symmetric encryption. The payload is signed using the device's long +// term private key for the target group. It also updates the chain key and +// stores the next message key in the cache. +func (s *secretStore) SealEnvelope(ctx context.Context, group *protocoltypes.Group, messagePayload []byte) ([]byte, error) { + if s == nil { + return nil, errcode.ErrInvalidInput.Wrap(fmt.Errorf("calling method of a non instantiated message keystore")) + } + + if s.deviceKeystore == nil { + return nil, errcode.ErrCryptoSignature.Wrap(fmt.Errorf("message keystore is opened in read-only mode")) + } + + if group == nil { + return nil, errcode.ErrInvalidInput.Wrap(fmt.Errorf("group cannot be nil")) + } + + localMemberDevice, err := s.deviceKeystore.memberDeviceForGroup(group) + if err != nil { + return nil, errcode.ErrGroupMemberUnknownGroupID.Wrap(err) + } + + groupPublicKey, err := group.GetPubKey() + if err != nil { + return nil, errcode.ErrDeserialization.Wrap(err) + } + + s.messageMutex.Lock() + defer s.messageMutex.Unlock() + + deviceChainKey, err := s.getDeviceChainKeyForGroupAndDevice(ctx, groupPublicKey, localMemberDevice.Device()) + if err != nil { + return nil, errcode.ErrInternal.Wrap(fmt.Errorf("unable to get device chainkey: %w", err)) + } + + env, err := sealEnvelope(messagePayload, deviceChainKey, localMemberDevice.device, group) + if err != nil { + return nil, errcode.ErrCryptoEncrypt.Wrap(fmt.Errorf("unable to seal envelope: %w", err)) + } + + if err := s.deriveDeviceChainKey(ctx, group, localMemberDevice.Device()); err != nil { + return nil, errcode.ErrCryptoKeyGeneration.Wrap(err) + } + + return env, nil +} + +// deriveDeviceChainKey derives the chain key value from the current one. +// It also updates the device chain key in the keystore. +func (s *secretStore) deriveDeviceChainKey(ctx context.Context, group *protocoltypes.Group, devicePublicKey crypto.PubKey) error { + if s == nil { + return errcode.ErrInvalidInput.Wrap(fmt.Errorf("calling method of a non instantiated message keystore")) + } + + if devicePublicKey == nil { + return errcode.ErrInvalidInput.Wrap(fmt.Errorf("device public key cannot be nil")) + } + + groupPublicKey, err := group.GetPubKey() + if err != nil { + return errcode.ErrDeserialization.Wrap(err) + } + + deviceChainKey, err := s.preComputeNextKey(ctx, groupPublicKey, devicePublicKey) + if err != nil { + return errcode.ErrCryptoKeyGeneration.Wrap(err) + } + + if err = s.updateCurrentKey(ctx, groupPublicKey, devicePublicKey, deviceChainKey); err != nil { + return errcode.ErrCryptoKeyGeneration.Wrap(err) + } + + return nil +} + +// updateCurrentKey updates the current device chain key in the keystore if the +// given device secret has a higher counter. +func (s *secretStore) updateCurrentKey(ctx context.Context, groupPublicKey crypto.PubKey, devicePublicKey crypto.PubKey, deviceChainKey *protocoltypes.DeviceChainKey) error { + if s == nil { + return errcode.ErrInvalidInput.Wrap(fmt.Errorf("calling method of a non instantiated message keystore")) + } + + currentDeviceChainKey, err := s.getDeviceChainKeyForGroupAndDevice(ctx, groupPublicKey, devicePublicKey) + if err != nil { + return errcode.ErrInternal.Wrap(err) + } + + // FIXME: counter is set randomly and can overflow to 0 + if deviceChainKey.Counter < currentDeviceChainKey.Counter { + return nil + } + + if err = s.putDeviceChainKey(ctx, groupPublicKey, devicePublicKey, deviceChainKey); err != nil { + return errcode.ErrInternal.Wrap(err) + } + + return nil +} + +// OutOfStoreMessageOpen opens the given OutOfStoreMessage and returns the +// decrypted payload. +// The signature is verified against the given groupPublicKey. +// It derives the next message key and stores it in the cache, but it doesn't +// update the device's chain key. +func (s *secretStore) OutOfStoreMessageOpen(ctx context.Context, envelope *protocoltypes.OutOfStoreMessage, groupPublicKey crypto.PubKey) ([]byte, bool, error) { + if s == nil { + return nil, false, errcode.ErrInvalidInput.Wrap(fmt.Errorf("calling method of a non instantiated message keystore")) + } + + if envelope == nil { + return nil, false, errcode.ErrInvalidInput.Wrap(fmt.Errorf("envelope cannot be nil")) + } + + if groupPublicKey == nil { + return nil, false, errcode.ErrInvalidInput.Wrap(fmt.Errorf("group public key cannot be nil")) + } + + devicePublicKey, err := crypto.UnmarshalEd25519PublicKey(envelope.DevicePK) + if err != nil { + return nil, false, errcode.ErrDeserialization.Wrap(err) + } + + c := cid.Undef + if len(envelope.CID) > 0 { + _, c, err = cid.CidFromBytes(envelope.CID) + if err != nil { + return nil, false, errcode.ErrDeserialization.Wrap(err) + } + } + + s.messageMutex.Lock() + defer s.messageMutex.Unlock() + + decryptionCtx := &decryptionContext{newlyDecrypted: true} + if decryptionCtx.messageKey, err = s.getKeyForCID(ctx, c); err == nil { + decryptionCtx.newlyDecrypted = false + } else { + decryptionCtx.messageKey, err = s.getPrecomputedMessageKey(ctx, groupPublicKey, devicePublicKey, envelope.Counter) + if err != nil { + return nil, false, errcode.ErrCryptoDecrypt.Wrap(err) + } + } + + clear, decryptionCtx, err := s.openPayloadWithMessageKey(decryptionCtx, devicePublicKey, envelope.EncryptedPayload, &protocoltypes.MessageHeaders{ + Counter: envelope.Counter, + DevicePK: envelope.DevicePK, + Sig: envelope.Sig, + }) + if err != nil { + return nil, false, errcode.ErrCryptoDecrypt.Wrap(err) + } + + if ok, err := devicePublicKey.Verify(clear, envelope.Sig); !ok { + return nil, false, errcode.ErrCryptoSignatureVerification.Wrap(fmt.Errorf("unable to verify message signature")) + } else if err != nil { + return nil, false, errcode.ErrCryptoSignatureVerification.Wrap(err) + } + + if _, err = s.preComputeNextKey(ctx, groupPublicKey, devicePublicKey); err != nil { + return nil, false, errcode.ErrInternal.Wrap(err) + } + + return clear, decryptionCtx.newlyDecrypted, nil +} + +// OutOfStoreGetGroupPublicKeyByGroupReference returns the group public key +// associated with the given out of store group reference (e.g. push +// notification payload). +func (s *secretStore) OutOfStoreGetGroupPublicKeyByGroupReference(ctx context.Context, ref []byte) (crypto.PubKey, error) { + s.messageMutex.RLock() + pk, err := s.datastore.Get(ctx, dsKeyForOutOfStoreMessageGroupHint(ref)) + s.messageMutex.RUnlock() + + if err != nil { + return nil, errcode.ErrNotFound.Wrap(err) + } + + groupPublicKey, err := crypto.UnmarshalEd25519PublicKey(pk) + if err != nil { + return nil, errcode.ErrDeserialization.Wrap(err) + } + + return groupPublicKey, nil +} + +// UpdateOutOfStoreGroupReferences updates the out of store (e.g. push +// notification payload) group references for the given devicePublicKey and +// groupPublicKey in the keystore. It creates the references for the +// given range [first + precomputeOutOfStoreGroupRefsCount] and +// [first - precomputeOutOfStoreGroupRefsCount] and deletes out of range +// references. +func (s *secretStore) UpdateOutOfStoreGroupReferences(ctx context.Context, devicePublicKey []byte, first uint64, group *protocoltypes.Group) error { + s.messageMutex.Lock() + defer s.messageMutex.Unlock() + + refsExisting := []uint64(nil) + refsToCreate := []uint64(nil) + + currentFirst, currentLast, err := s.firstLastCachedGroupRefsForMember(ctx, devicePublicKey, group) + if err == nil { + for i := currentFirst; i != currentLast; i++ { + refsExisting = append(refsExisting, i) + } + } + + // keep previous refs + last := first + s.precomputeOutOfStoreGroupRefsCount + first -= s.precomputeOutOfStoreGroupRefsCount + for i := first; i != last; i++ { + found := false + + // Ignore refs that should be kept + for j := 0; j < len(refsExisting); j++ { + if refsExisting[j] == i { + refsExisting[j] = refsExisting[len(refsExisting)-1] + refsExisting = refsExisting[:len(refsExisting)-1] + found = true + break + } + } + + if !found { + refsToCreate = append(refsToCreate, i) + } + } + + // Remove useless old refs + for i := 0; i < len(refsExisting); i++ { + ref, err := createOutOfStoreGroupReference(group, devicePublicKey, refsExisting[i]) + if err != nil { + s.logger.Error("creating existing out of store group reference failed", logutil.PrivateBinary("ref", ref), zap.Error(err)) + continue + } + + if err := s.datastore.Delete(ctx, dsKeyForOutOfStoreMessageGroupHint(ref)); err != nil { + s.logger.Error("deleting existing out of store group reference failed", logutil.PrivateBinary("ref", ref), zap.Error(err)) + continue + } + } + + // Add new refs + for i := 0; i < len(refsToCreate); i++ { + ref, err := createOutOfStoreGroupReference(group, devicePublicKey, refsToCreate[i]) + if err != nil { + s.logger.Error("creating new out of store group reference failed", logutil.PrivateBinary("ref", ref), zap.Error(err)) + continue + } + + if err := s.datastore.Put(ctx, dsKeyForOutOfStoreMessageGroupHint(ref), group.GetPublicKey()); err != nil { + s.logger.Error("putting new out of store group reference failed", logutil.PrivateBinary("ref", ref), zap.Error(err)) + continue + } + } + + // Update first/last + if err := s.putFirstLastCachedGroupRefsForMember(ctx, first, last, devicePublicKey, group); err != nil { + s.logger.Error("putting first/last out of store group reference failed", zap.Error(err)) + } + + return nil +} + +// firstLastCachedGroupRefsForMember returns the first and last cached group +// references counter for the given devicePublicKey and group. +func (s *secretStore) firstLastCachedGroupRefsForMember(ctx context.Context, devicePublicKeyBytes []byte, group *protocoltypes.Group) (uint64, uint64, error) { + key := dsKeyForOutOfStoreFirstLastCounters(group.GetPublicKey(), devicePublicKeyBytes) + + // No mutex here + bytes, err := s.datastore.Get(ctx, key) + if err != nil { + return 0, 0, errcode.ErrDBRead.Wrap(err) + } + + ret := protocoltypes.FirstLastCounters{} + if err := ret.Unmarshal(bytes); err != nil { + return 0, 0, errcode.ErrDeserialization.Wrap(err) + } + + return ret.First, ret.Last, nil +} + +// putFirstLastCachedGroupRefsForMember puts the first and last cached group +// references counter for the given devicePK and groupPK. +func (s *secretStore) putFirstLastCachedGroupRefsForMember(ctx context.Context, first uint64, last uint64, devicePublicKey []byte, group *protocoltypes.Group) error { + key := dsKeyForOutOfStoreFirstLastCounters(group.GetPublicKey(), devicePublicKey) + + fistLast := protocoltypes.FirstLastCounters{ + First: first, + Last: last, + } + bytes, err := fistLast.Marshal() + if err != nil { + return errcode.ErrSerialization.Wrap(err) + } + + // Not mutex here + return s.datastore.Put(ctx, key, bytes) +} + +func sealPayload(payload []byte, ds *protocoltypes.DeviceChainKey, devicePrivateKey crypto.PrivKey, g *protocoltypes.Group) ([]byte, []byte, error) { + var ( + msgKey [32]byte + err error + ) + + sig, err := devicePrivateKey.Sign(payload) + if err != nil { + return nil, nil, errcode.ErrCryptoSignature.Wrap(err) + } + + if _, msgKey, err = deriveNextKeys(ds.ChainKey, nil, g.GetPublicKey()); err != nil { + return nil, nil, errcode.ErrCryptoKeyGeneration.Wrap(err) + } + + return secretbox.Seal(nil, payload, uint64AsNonce(ds.Counter+1), &msgKey), sig, nil +} + +func sealEnvelope(messagePayload []byte, deviceChainKey *protocoltypes.DeviceChainKey, devicePrivateKey crypto.PrivKey, g *protocoltypes.Group) ([]byte, error) { + encryptedPayload, sig, err := sealPayload(messagePayload, deviceChainKey, devicePrivateKey, g) + if err != nil { + return nil, errcode.ErrCryptoEncrypt.Wrap(err) + } + + devicePublicKeyRaw, err := devicePrivateKey.GetPublic().Raw() + if err != nil { + return nil, errcode.ErrSerialization.Wrap(err) + } + + h := &protocoltypes.MessageHeaders{ + Counter: deviceChainKey.Counter + 1, + DevicePK: devicePublicKeyRaw, + Sig: sig, + } + + headers, err := proto.Marshal(h) + if err != nil { + return nil, errcode.ErrSerialization.Wrap(err) + } + + nonce, err := cryptoutil.GenerateNonce() + if err != nil { + return nil, errcode.ErrCryptoNonceGeneration.Wrap(err) + } + + encryptedHeaders := secretbox.Seal(nil, headers, nonce, g.GetSharedSecret()) + + env, err := proto.Marshal(&protocoltypes.MessageEnvelope{ + MessageHeaders: encryptedHeaders, + Message: encryptedPayload, + Nonce: nonce[:], + }) + if err != nil { + return nil, errcode.ErrSerialization.Wrap(err) + } + + return env, nil +} + +// nolint:unparam +func deriveNextKeys(chainKeyValue []byte, salt []byte, groupID []byte) ([]byte, messageKey, error) { + var ( + nextMsg [32]byte + err error + ) + + // Salt length must be equal to hash length (64 bytes for sha256) + hash := sha256.New + + // Generate Pseudo Random Key using chainKeyValue as IKM and salt + prk := hkdf.Extract(hash, chainKeyValue, salt) + if len(prk) == 0 { + return nil, nextMsg, errcode.ErrInternal.Wrap(fmt.Errorf("unable to instantiate pseudo random key")) + } + + // Expand using extracted prk and groupID as info (kind of namespace) + kdf := hkdf.Expand(hash, prk, groupID) + + // Generate next KDF and message keys + nextCK, err := ioutil.ReadAll(io.LimitReader(kdf, 32)) + if err != nil { + return nil, nextMsg, errcode.ErrCryptoKeyGeneration.Wrap(err) + } + + nextMsgSlice, err := ioutil.ReadAll(io.LimitReader(kdf, 32)) + if err != nil { + return nil, nextMsg, errcode.ErrCryptoKeyGeneration.Wrap(err) + } + + copy(nextMsg[:], nextMsgSlice) + + return nextCK, nextMsg, nil +} + +func uint64AsNonce(val uint64) *[24]byte { + var nonce [24]byte + + binary.BigEndian.PutUint64(nonce[:], val) + + return &nonce +} diff --git a/pkg/secretstore/secret_store_messages_test.go b/pkg/secretstore/secret_store_messages_test.go new file mode 100644 index 00000000..ef78d6fd --- /dev/null +++ b/pkg/secretstore/secret_store_messages_test.go @@ -0,0 +1,244 @@ +package secretstore_test + +import ( + "context" + "fmt" + "os" + "path" + "testing" + + cid "github.com/ipfs/go-cid" + "github.com/libp2p/go-libp2p/core/crypto" + "github.com/stretchr/testify/assert" + + "berty.tech/weshnet" + "berty.tech/weshnet/pkg/protocoltypes" + "berty.tech/weshnet/pkg/secretstore" + "berty.tech/weshnet/pkg/testutil" +) + +func addDummyMemberInMetadataStore(ctx context.Context, t testing.TB, ms *weshnet.MetadataStore, g *protocoltypes.Group, memberPK crypto.PubKey, join bool) crypto.PubKey { + t.Helper() + + secretStore, err := secretstore.NewInMemSecretStore(nil) + assert.NoError(t, err) + assert.NotNil(t, secretStore) + + md, err := secretStore.GetOwnMemberDeviceForGroup(g) + assert.NoError(t, err) + + if join { + _, err = weshnet.MetadataStoreAddDeviceToGroup(ctx, ms, g, md) + assert.NoError(t, err) + } + + deviceChainKeyToSend, err := secretStore.GetShareableChainKey(ctx, g, memberPK) + assert.NoError(t, err) + + _, err = weshnet.MetadataStoreSendSecret(ctx, ms, g, md, memberPK, deviceChainKeyToSend) + assert.NoError(t, err) + + return md.Device() +} + +func Test_EncryptMessageEnvelope(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + g, _, err := weshnet.NewGroupMultiMember() + assert.NoError(t, err) + + secretStore1, err := secretstore.NewInMemSecretStore(nil) + assert.NoError(t, err) + assert.NotNil(t, secretStore1) + + t.Cleanup(func() { + _ = secretStore1.Close() + }) + + omd1, err := secretStore1.GetOwnMemberDeviceForGroup(g) + assert.NoError(t, err) + + gc1 := weshnet.NewContextGroup(g, nil, nil, secretStore1, omd1, nil) + + deviceChainKey1For1, err := secretStore1.GetShareableChainKey(ctx, g, gc1.MemberPubKey()) + assert.NoError(t, err) + + err = secretStore1.RegisterChainKey(ctx, g, gc1.DevicePubKey(), deviceChainKey1For1) + assert.NoError(t, err) + + secretStore2, err := secretstore.NewInMemSecretStore(nil) + assert.NoError(t, err) + assert.NotNil(t, secretStore2) + + t.Cleanup(func() { + _ = secretStore2.Close() + }) + + omd2, err := secretStore2.GetOwnMemberDeviceForGroup(g) + assert.NoError(t, err) + + payloadRef1, err := (&protocoltypes.EncryptedMessage{Plaintext: []byte("Test payload 1")}).Marshal() + assert.NoError(t, err) + + deviceChainKey1For2, err := secretStore1.GetShareableChainKey(ctx, g, omd2.Member()) + assert.NoError(t, err) + + deviceChainKey2For2, err := secretStore2.GetShareableChainKey(ctx, g, omd2.Member()) + assert.NoError(t, err) + + err = secretStore2.RegisterChainKey(ctx, g, omd2.Device(), deviceChainKey2For2) + assert.NoError(t, err) + + err = secretStore2.RegisterChainKey(ctx, g, omd1.Device(), deviceChainKey1For2) + assert.NoError(t, err) + + env1, err := secretStore1.SealEnvelope(ctx, g, payloadRef1) + assert.NoError(t, err) + + headers, payloadClr1, err := openEnvelope(ctx, t, secretStore2, g, omd2.Device(), env1, cid.Undef) + assert.NoError(t, err) + + devRaw, err := omd1.Device().Raw() + assert.Equal(t, headers.DevicePK, devRaw) + + payloadClrlBytes, err := payloadClr1.Marshal() + assert.NoError(t, err) + assert.Equal(t, payloadRef1, payloadClrlBytes) +} + +func testMessageKeyHolderCatchUp(t *testing.T, expectedNewDevices int, isSlow bool) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + if isSlow { + testutil.FilterSpeed(t, testutil.Slow) + } + + dir := path.Join(os.TempDir(), fmt.Sprintf("%d", os.Getpid()), "MessageKeyHolderCatchUp") + defer os.RemoveAll(dir) + + peers, _, cleanup := weshnet.CreatePeersWithGroupTest(ctx, t, dir, 1, 1) + defer cleanup() + + peer := peers[0] + + secretStore1 := peer.SecretStore + ms1 := peer.GC.MetadataStore() + + groupPublicKey, err := peer.GC.Group().GetPubKey() + assert.NoError(t, err) + + devicesPK := make([]crypto.PubKey, expectedNewDevices) + + for i := 0; i < expectedNewDevices; i++ { + devicesPK[i] = addDummyMemberInMetadataStore(ctx, t, ms1, peer.GC.Group(), peer.GC.MemberPubKey(), true) + } + + for range peer.GC.FillMessageKeysHolderUsingPreviousData() { + } + + for i, devicePublicKey := range devicesPK { + if !assert.True(t, secretStore1.IsChainKeyKnownForDevice(ctx, groupPublicKey, devicePublicKey)) { + t.Fatalf("failed at iteration %d", i) + } + } +} + +func TestMessageKeyHolderCatchUp(t *testing.T) { + for _, testCase := range []struct { + expectedNewDevices int + slow bool + }{ + { + expectedNewDevices: 2, + slow: false, + }, + { + expectedNewDevices: 10, + slow: true, + }, + } { + testMessageKeyHolderCatchUp(t, testCase.expectedNewDevices, testCase.slow) + } +} + +func testMessageKeyHolderSubscription(t *testing.T, expectedNewDevices int, isSlow bool) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + if isSlow { + testutil.FilterSpeed(t, testutil.Slow) + } + + dir := path.Join(os.TempDir(), fmt.Sprintf("%d", os.Getpid()), "MessageKeyHolderSubscription") + defer os.RemoveAll(dir) + + peers, groupPrivateKey, cleanup := weshnet.CreatePeersWithGroupTest(ctx, t, dir, 1, 1) + defer cleanup() + + peer := peers[0] + + secretStore1 := peer.SecretStore + ms1 := peer.GC.MetadataStore() + + devicesPK := make([]crypto.PubKey, expectedNewDevices) + + ch := peer.GC.FillMessageKeysHolderUsingNewData() + + for i := 0; i < expectedNewDevices; i++ { + devicesPK[i] = addDummyMemberInMetadataStore(ctx, t, ms1, peer.GC.Group(), peer.GC.MemberPubKey(), true) + } + + i := 0 + for range ch { + i++ + if i == expectedNewDevices { + break + } + } + + for i, devicePublicKey := range devicesPK { + if !assert.True(t, secretStore1.IsChainKeyKnownForDevice(ctx, groupPrivateKey.GetPublic(), devicePublicKey)) { + t.Fatalf("failed at iteration %d", i) + } + } +} + +func TestMessageKeyHolderSubscription(t *testing.T) { + for _, testCase := range []struct { + expectedNewDevices int + slow bool + }{ + { + expectedNewDevices: 2, + slow: false, + }, + { + expectedNewDevices: 10, + slow: true, + }, + } { + testMessageKeyHolderSubscription(t, testCase.expectedNewDevices, testCase.slow) + } +} + +// openEnvelope opens a MessageEnvelope and returns the decrypted message. +// It performs all the necessary steps to decrypt the message. +func openEnvelope(ctx context.Context, t testing.TB, secretStore secretstore.SecretStore, g *protocoltypes.Group, ownPK crypto.PubKey, data []byte, id cid.Cid) (*protocoltypes.MessageHeaders, *protocoltypes.EncryptedMessage, error) { + t.Helper() + + assert.NotNil(t, secretStore) + assert.NotNil(t, g) + + env, headers, err := secretStore.OpenEnvelopeHeaders(data, g) + assert.NoError(t, err) + + gPK, err := g.GetPubKey() + assert.NoError(t, err) + + msg, err := secretStore.OpenEnvelopePayload(ctx, env, headers, gPK, ownPK, id) + assert.NoError(t, err) + + return headers, msg, nil +} diff --git a/pkg/secretstore/secret_store_test.go b/pkg/secretstore/secret_store_test.go new file mode 100644 index 00000000..37ff1bf0 --- /dev/null +++ b/pkg/secretstore/secret_store_test.go @@ -0,0 +1,567 @@ +package secretstore + +import ( + "context" + crand "crypto/rand" + "encoding/hex" + "fmt" + "testing" + + "github.com/ipfs/go-cid" + "github.com/ipfs/go-datastore" + "github.com/libp2p/go-libp2p/core/crypto" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "berty.tech/weshnet/pkg/protocoltypes" +) + +func Test_PushGroupReferences(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + g, _, err := protocoltypes.NewGroupMultiMember() + require.NoError(t, err) + + otherSecretStore, err := newInMemSecretStore(nil) + require.NoError(t, err) + t.Cleanup(func() { + _ = otherSecretStore.Close() + }) + + ownSecretStore, err := newInMemSecretStore(nil) + require.NoError(t, err) + t.Cleanup(func() { + _ = ownSecretStore.Close() + }) + + otherMemberDevice, err := otherSecretStore.GetOwnMemberDeviceForGroup(g) + require.NoError(t, err) + + otherDevicePK, err := otherMemberDevice.Device().Raw() + require.NoError(t, err) + + deviceChainKey, err := newDeviceChainKey() + require.NoError(t, err) + + // test with the deviceChainKey counter + updateAndTestPushGroupReferences(ctx, ownSecretStore, otherDevicePK, deviceChainKey.Counter, g, t) + + // do the same test with a new device chain key counter, + // so we can test if old references are deleted + updateAndTestPushGroupReferences(ctx, ownSecretStore, otherDevicePK, deviceChainKey.Counter+10, g, t) +} + +func updateAndTestPushGroupReferences(ctx context.Context, secretStore *secretStore, devicePK []byte, counter uint64, g *protocoltypes.Group, t *testing.T) { + // update the push group references + err := secretStore.UpdateOutOfStoreGroupReferences(ctx, devicePK, counter, g) + require.NoError(t, err) + + // test that the push group references are updated + // refs start counter - 100 to counter + 100 + start := counter - PrecomputeOutOfStoreGroupRefsCount + end := counter + PrecomputeOutOfStoreGroupRefsCount + + for i := start; i < end; i++ { + // compute the push group reference + pushGroupRef, err := createOutOfStoreGroupReference(g, devicePK, i) + require.NoError(t, err) + + _, err = secretStore.OutOfStoreGetGroupPublicKeyByGroupReference(ctx, pushGroupRef) + require.NoError(t, err, fmt.Sprintf("started at %d, failed as %d", start, i)) + } + + // test boundary conditions + + // before the start counter + { + before := counter - PrecomputeOutOfStoreGroupRefsCount - 1 + pushGroupRef, err := createOutOfStoreGroupReference(g, devicePK, before) + require.NoError(t, err) + _, err = secretStore.OutOfStoreGetGroupPublicKeyByGroupReference(ctx, pushGroupRef) + require.Error(t, err) + } + + // after the end counter + { + end := counter + PrecomputeOutOfStoreGroupRefsCount + 1 + pushGroupRef, err := createOutOfStoreGroupReference(g, devicePK, end) + require.NoError(t, err) + _, err = secretStore.OutOfStoreGetGroupPublicKeyByGroupReference(ctx, pushGroupRef) + require.Error(t, err) + } +} + +func Test_SealOutOfStoreMessageEnvelope_OpenOutOfStoreMessage(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + g, _, err := protocoltypes.NewGroupMultiMember() + require.NoError(t, err) + + accUnrelated, err := newInMemSecretStore(nil) + require.NoError(t, err) + require.NotNil(t, accUnrelated) + t.Cleanup(func() { + _ = accUnrelated.Close() + }) + + err = accUnrelated.PutGroup(ctx, g) + require.NoError(t, err) + + acc1, err := newInMemSecretStore(nil) + require.NoError(t, err) + t.Cleanup(func() { + _ = acc1.Close() + }) + + acc2, err := newInMemSecretStore(nil) + require.NoError(t, err) + t.Cleanup(func() { + _ = acc1.Close() + }) + + memberDevice1ForGroup, err := acc1.GetOwnMemberDeviceForGroup(g) + require.NoError(t, err) + + memberDevice2ForGroup, err := acc2.GetOwnMemberDeviceForGroup(g) + require.NoError(t, err) + + deviceChainKey1For2, err := acc1.GetShareableChainKey(ctx, g, memberDevice2ForGroup.Member()) + require.NoError(t, err) + + testPayload := []byte("test payload") + + err = acc2.RegisterChainKey(ctx, g, memberDevice1ForGroup.Device(), deviceChainKey1For2) + require.NoError(t, err) + + envEncrypted, err := acc1.SealEnvelope(ctx, g, testPayload) + require.NoError(t, err) + + env, headers, err := ((*secretStore)(nil)).OpenEnvelopeHeaders(envEncrypted, g) + require.NoError(t, err) + + outOfStoreEnv, err := (*secretStore)(nil).SealOutOfStoreMessageEnvelope(cid.Undef, env, headers, g) + require.NoError(t, err) + + groupPublicKey, err := acc2.OutOfStoreGetGroupPublicKeyByGroupReference(ctx, outOfStoreEnv.GroupReference) + require.NoError(t, err) + + outOfStoreMessage, err := accUnrelated.decryptOutOfStoreMessageEnv(ctx, outOfStoreEnv, groupPublicKey) + require.NoError(t, err) + + payload, newlyDecrypted, err := acc2.OutOfStoreMessageOpen(ctx, outOfStoreMessage, groupPublicKey) + require.NoError(t, err) + require.True(t, newlyDecrypted) + require.Equal(t, testPayload, payload) +} + +func Test_OutOfStoreDeserialize(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + secretStore1, err := NewSecretStore(datastore.NewMapDatastore(), nil) + require.NoError(t, err) + + t.Cleanup(func() { _ = secretStore1.Close() }) + + secretStore2, err := NewSecretStore(datastore.NewMapDatastore(), nil) + require.NoError(t, err) + + t.Cleanup(func() { _ = secretStore2.Close() }) + + group, _, err := protocoltypes.NewGroupMultiMember() + require.NoError(t, err) + + require.NoError(t, secretStore1.PutGroup(ctx, group)) + require.NoError(t, secretStore2.PutGroup(ctx, group)) + + memberDevice1, err := secretStore1.GetOwnMemberDeviceForGroup(group) + require.NoError(t, err) + + memberDevice2, err := secretStore2.GetOwnMemberDeviceForGroup(group) + require.NoError(t, err) + + chainKey1For2, err := secretStore1.GetShareableChainKey(ctx, group, memberDevice2.Member()) + require.NoError(t, err) + + chainKey2For1, err := secretStore2.GetShareableChainKey(ctx, group, memberDevice1.Member()) + require.NoError(t, err) + + require.NoError(t, secretStore1.RegisterChainKey(ctx, group, memberDevice2.Device(), chainKey2For1)) + require.NoError(t, secretStore2.RegisterChainKey(ctx, group, memberDevice1.Device(), chainKey1For2)) + + testPayload := []byte("test payload") + + env, err := secretStore1.SealEnvelope(ctx, group, testPayload) + require.NoError(t, err) + + envHeaders, msgHeaders, err := secretStore1.OpenEnvelopeHeaders(env, group) + require.NoError(t, err) + + dummyCID, err := cid.Parse("QmNR2n4zywCV61MeMLB6JwPueAPqheqpfiA4fLPMxouEmQ") + require.NoError(t, err) + + device1Raw, err := memberDevice1.Device().Raw() + require.NoError(t, err) + + outOfStoreMessageEnvelope, err := secretStore1.SealOutOfStoreMessageEnvelope(dummyCID, envHeaders, msgHeaders, group) + require.NoError(t, err) + + outOfStoreMessageEnvelopeBytes, err := outOfStoreMessageEnvelope.Marshal() + require.NoError(t, err) + + // Attempting to decrypt the message without a relay + { + openedOutOfStoreMessage, groupFound, clearPayload, alreadyDecrypted, err := secretStore2.OpenOutOfStoreMessage(ctx, outOfStoreMessageEnvelopeBytes) + require.NoError(t, err) + + require.Equal(t, testPayload, clearPayload) + require.Equal(t, device1Raw, openedOutOfStoreMessage.DevicePK) + require.Equal(t, dummyCID.Bytes(), openedOutOfStoreMessage.CID) + require.Equal(t, group.PublicKey, groupFound.PublicKey) + require.False(t, alreadyDecrypted) + } +} + +func Test_EncryptMessageEnvelopeAndDerive(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + g, _, err := protocoltypes.NewGroupMultiMember() + assert.NoError(t, err) + + mkh1, err := newInMemSecretStore(nil) + assert.NoError(t, err) + t.Cleanup(func() { + _ = mkh1.Close() + }) + + mkh2, err := newInMemSecretStore(nil) + assert.NoError(t, err) + t.Cleanup(func() { + _ = mkh1.Close() + }) + + omd1, err := mkh1.GetOwnMemberDeviceForGroup(g) + assert.NoError(t, err) + + omd2, err := mkh2.GetOwnMemberDeviceForGroup(g) + assert.NoError(t, err) + + gPK, err := g.GetPubKey() + assert.NoError(t, err) + + gc1DevicePubKey := omd1.Device() + + ds1For1Encrypted, err := mkh1.GetShareableChainKey(ctx, g, omd1.Member()) + assert.NoError(t, err) + + ds1For2Encrypted, err := mkh1.GetShareableChainKey(ctx, g, omd2.Member()) + assert.NoError(t, err) + + ds2For2Encrypted, err := mkh2.GetShareableChainKey(ctx, g, omd2.Member()) + assert.NoError(t, err) + + err = mkh1.RegisterChainKey(ctx, g, omd1.Device(), ds1For1Encrypted) + assert.NoError(t, err) + + err = mkh2.RegisterChainKey(ctx, g, omd2.Device(), ds2For2Encrypted) + assert.NoError(t, err) + + err = mkh2.RegisterChainKey(ctx, g, omd1.Device(), ds1For2Encrypted) + assert.NoError(t, err) + + ds1, err := mkh1.getOwnDeviceChainKeyForGroup(ctx, g) + assert.NoError(t, err) + + initialCounter := ds1.Counter + + for i := 0; i < 1000; i++ { + payloadRef, err := (&protocoltypes.EncryptedMessage{Plaintext: []byte("Test payload 1")}).Marshal() + assert.NoError(t, err) + envEncrypted, err := mkh1.SealEnvelope(ctx, g, payloadRef) + assert.NoError(t, err) + + ds, err := mkh1.getDeviceChainKeyForGroupAndDevice(ctx, gPK, gc1DevicePubKey) + if !assert.NoError(t, err) { + t.Fatalf("failed at i = %d", i) + } + assert.Equal(t, ds.Counter, initialCounter+uint64(i+1)) + + env, headers, err := ((*secretStore)(nil)).OpenEnvelopeHeaders(envEncrypted, g) + if !assert.NoError(t, err) { + t.Fatalf("failed at i = %d", i) + } + + payloadClr, err := mkh2.OpenEnvelopePayload(ctx, env, headers, gPK, omd2.Device(), cid.Undef) + if !assert.NoError(t, err) { + t.Fatalf("failed at i = %d", i) + } + + if assert.NotNil(t, headers) && assert.NotNil(t, payloadClr) { + devRaw, err := omd1.Device().Raw() + assert.NoError(t, err) + + assert.Equal(t, headers.DevicePK, devRaw) + + payloadClrBytes, err := payloadClr.Marshal() + assert.NoError(t, err) + assert.Equal(t, payloadRef, payloadClrBytes) + } else { + break + } + } +} + +func mustDeviceChainKey(t testing.TB) func(ds *protocoltypes.DeviceChainKey, err error) *protocoltypes.DeviceChainKey { + return func(ds *protocoltypes.DeviceChainKey, err error) *protocoltypes.DeviceChainKey { + t.Helper() + + if err != nil { + t.Fatal(err) + } + + return ds + } +} + +func mustMessageHeaders(t testing.TB, omd OwnMemberDevice, counter uint64, payload []byte) *protocoltypes.MessageHeaders { + t.Helper() + + pkB, err := omd.Device().Raw() + if err != nil { + t.Fatal(err) + } + + sig, err := omd.DeviceSign(payload) + if err != nil { + t.Fatal(err) + } + + return &protocoltypes.MessageHeaders{ + Counter: counter, + DevicePK: pkB, + Sig: sig, + } +} + +func Test_EncryptMessagePayload(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + group, _, err := protocoltypes.NewGroupMultiMember() + assert.NoError(t, err) + + mkh1, err := newInMemSecretStore(nil) + assert.NoError(t, err) + t.Cleanup(func() { + mkh1.Close() + }) + + mkh2, err := newInMemSecretStore(nil) + assert.NoError(t, err) + t.Cleanup(func() { + mkh2.Close() + }) + + omd1, err := mkh1.GetOwnMemberDeviceForGroup(group) + assert.NoError(t, err) + + omd2, err := mkh2.GetOwnMemberDeviceForGroup(group) + assert.NoError(t, err) + + encryptedDS1For1, err := mkh1.GetShareableChainKey(ctx, group, omd1.Member()) + assert.NoError(t, err) + + encryptedDS1For2, err := mkh1.GetShareableChainKey(ctx, group, omd2.Member()) + assert.NoError(t, err) + + encryptedDS2For2, err := mkh2.GetShareableChainKey(ctx, group, omd2.Member()) + assert.NoError(t, err) + + gc1DevicePubKey := omd1.Device() + gc2DevicePubKey := omd2.Device() + + err = mkh1.RegisterChainKey(ctx, group, gc1DevicePubKey, encryptedDS1For1) + assert.NoError(t, err) + + err = mkh2.RegisterChainKey(ctx, group, gc2DevicePubKey, encryptedDS2For2) + assert.NoError(t, err) + + ds1, err := mkh1.getOwnDeviceChainKeyForGroup(ctx, group) + assert.NoError(t, err) + + initialCounter := ds1.Counter + firstDeviceChainKey := append([]byte(nil), ds1.ChainKey...) + + payloadRef1 := []byte("ok, this is the first test") + payloadRef2 := []byte("so, this is a second test") + payloadRef3 := []byte("this will be posted many times") + + err = mkh2.RegisterChainKey(ctx, group, omd1.Device(), encryptedDS1For2) + assert.NoError(t, err) + + gPK, err := group.GetPubKey() + assert.NoError(t, err) + + assert.Equal(t, mustDeviceChainKey(t)(mkh1.getDeviceChainKeyForGroupAndDevice(ctx, gPK, omd1.Device())).ChainKey, firstDeviceChainKey) + + payloadEnc1, _, err := sealPayload(payloadRef1, mustDeviceChainKey(t)(mkh1.getDeviceChainKeyForGroupAndDevice(ctx, gPK, omd1.Device())), omd1.(*ownMemberDevice).device, group) + assert.NoError(t, err) + + // secret is derived by sealEnvelope + err = mkh1.deriveDeviceChainKey(ctx, group, omd1.Device()) + assert.NoError(t, err) + + assert.NotEqual(t, hex.EncodeToString(payloadRef1), hex.EncodeToString(payloadEnc1)) + + // Messages are encrypted with DeviceChainKey.Counter + // uint64 overflows to 0, which is the expected behaviour + + // Test with a wrong counter value + payloadClr1, decryptInfo, err := mkh2.openPayload(ctx, cid.Undef, gPK, payloadEnc1, mustMessageHeaders(t, omd1, initialCounter+2, payloadRef1)) + assert.Error(t, err) + assert.Nil(t, decryptInfo) + assert.Equal(t, "", string(payloadClr1)) + + // Test with a valid counter value, but no CID (so no cache) + payloadClr1, decryptInfo, err = mkh2.openPayload(ctx, cid.Undef, gPK, payloadEnc1, mustMessageHeaders(t, omd1, initialCounter+1, payloadRef1)) + assert.NoError(t, err) + assert.Equal(t, string(payloadRef1), string(payloadClr1)) + + err = mkh2.postDecryptActions(ctx, decryptInfo, gPK, omd2.Device(), mustMessageHeaders(t, omd1, initialCounter+1, payloadRef1)) + assert.NoError(t, err) + + ds, err := mkh1.getDeviceChainKeyForGroupAndDevice(ctx, gPK, omd1.Device()) + assert.NoError(t, err) + + assert.Equal(t, ds.Counter, initialCounter+1) + assert.NotEqual(t, ds.ChainKey, firstDeviceChainKey) + + payloadEnc2, _, err := sealPayload(payloadRef1, ds, omd1.(*ownMemberDevice).device, group) + assert.NoError(t, err) + + err = mkh1.deriveDeviceChainKey(ctx, group, omd1.Device()) + assert.NoError(t, err) + + // Ensure that encrypted message is not the same as the first message + assert.NotEqual(t, hex.EncodeToString(payloadRef1), hex.EncodeToString(payloadEnc2)) + assert.NotEqual(t, hex.EncodeToString(payloadEnc1), hex.EncodeToString(payloadEnc2)) + + payloadClr2, decryptInfo, err := mkh2.openPayload(ctx, cid.Undef, gPK, payloadEnc2, mustMessageHeaders(t, omd1, initialCounter+2, payloadRef1)) + assert.NoError(t, err) + + err = mkh2.postDecryptActions(ctx, decryptInfo, gPK, omd2.Device(), mustMessageHeaders(t, omd1, initialCounter+2, payloadRef1)) + assert.NoError(t, err) + + assert.Equal(t, string(payloadRef1), string(payloadClr2)) + + // Make sure that a message without a CID can't be decrypted twice + payloadClr2, decryptInfo, err = mkh2.openPayload(ctx, cid.Undef, gPK, payloadEnc2, mustMessageHeaders(t, omd1, initialCounter+1, payloadRef1)) + assert.Error(t, err) + assert.Equal(t, "", string(payloadClr2)) + + ds, err = mkh1.getDeviceChainKeyForGroupAndDevice(ctx, gPK, omd1.Device()) + assert.NoError(t, err) + + // Make sure that a message a CID can be decrypted twice + payloadEnc3, _, err := sealPayload(payloadRef2, ds, omd1.(*ownMemberDevice).device, group) + assert.NoError(t, err) + + err = mkh1.deriveDeviceChainKey(ctx, group, omd1.Device()) + assert.NoError(t, err) + + dummyCID1, err := cid.Parse("QmbdQXQh9B2bWZgZJqfbjNPV5jGN2owbQ3vjeYsaDaCDqU") + assert.NoError(t, err) + + dummyCID2, err := cid.Parse("Qmf8oj9wbfu73prNAA1cRQVDqA52gD5B3ApnYQQjcjffH4") + assert.NoError(t, err) + + // Not decrypted message yet, wrong counter value + payloadClr3, decryptInfo, err := mkh2.openPayload(ctx, dummyCID1, gPK, payloadEnc3, mustMessageHeaders(t, omd1, initialCounter+2, payloadRef2)) + assert.Error(t, err) + assert.Equal(t, "", string(payloadClr3)) + + payloadClr3, decryptInfo, err = mkh2.openPayload(ctx, dummyCID1, gPK, payloadEnc3, mustMessageHeaders(t, omd1, initialCounter+3, payloadRef2)) + assert.NoError(t, err) + assert.Equal(t, string(payloadRef2), string(payloadClr3)) + + err = mkh2.postDecryptActions(ctx, decryptInfo, gPK, omd2.Device(), mustMessageHeaders(t, omd1, initialCounter+3, payloadRef2)) + assert.NoError(t, err) + + payloadClr3, decryptInfo, err = mkh2.openPayload(ctx, dummyCID1, gPK, payloadEnc3, mustMessageHeaders(t, omd1, initialCounter+3, payloadRef2)) + assert.NoError(t, err) + assert.Equal(t, string(payloadRef2), string(payloadClr3)) + + err = mkh2.postDecryptActions(ctx, decryptInfo, gPK, omd2.Device(), mustMessageHeaders(t, omd1, initialCounter+3, payloadRef2)) + assert.NoError(t, err) + + // Wrong CID + payloadClr3, decryptInfo, err = mkh2.openPayload(ctx, dummyCID2, gPK, payloadEnc3, mustMessageHeaders(t, omd1, initialCounter+3, payloadRef2)) + assert.Error(t, err) + assert.Equal(t, "", string(payloadClr3)) + + // Reused CID, wrong counter value + payloadClr3, decryptInfo, err = mkh2.openPayload(ctx, dummyCID1, gPK, payloadEnc3, mustMessageHeaders(t, omd1, initialCounter+4, payloadRef2)) + assert.Error(t, err) + assert.Equal(t, "", string(payloadClr3)) + + massExpected := uint64(200) + + // Test appending 200 messages, to ensure new secrets are generated correctly + for i := uint64(0); i < massExpected; i++ { + ds, err = mkh1.getDeviceChainKeyForGroupAndDevice(ctx, gPK, omd1.Device()) + assert.NoError(t, err) + + payloadEnc, _, err := sealPayload(payloadRef3, ds, omd1.(*ownMemberDevice).device, group) + assert.NoError(t, err) + + err = mkh1.deriveDeviceChainKey(ctx, group, omd1.Device()) + assert.NoError(t, err) + + ds, err = mkh1.getDeviceChainKeyForGroupAndDevice(ctx, gPK, omd1.Device()) + assert.NoError(t, err) + + counter := ds.Counter + + payloadClr, decryptInfo, err := mkh2.openPayload(ctx, cid.Undef, gPK, payloadEnc, mustMessageHeaders(t, omd1, counter, payloadRef3)) + if !assert.NoError(t, err) { + t.Fatalf("failed at i = %d", i) + } + + err = mkh2.postDecryptActions(ctx, decryptInfo, gPK, omd2.Device(), mustMessageHeaders(t, omd1, counter, payloadRef3)) + assert.NoError(t, err) + + assert.Equal(t, string(payloadRef3), string(payloadClr)) + } + + ds, err = mkh1.getDeviceChainKeyForGroupAndDevice(ctx, gPK, omd1.Device()) + assert.NoError(t, err) + + assert.Equal(t, initialCounter+massExpected+3, ds.Counter) +} + +func TestGetGroupForContact(t *testing.T) { + privateKey, _, err := crypto.GenerateEd25519Key(crand.Reader) + require.NoError(t, err) + + group, err := getGroupForContact(privateKey) + require.NoError(t, err) + + require.Equal(t, group.GroupType, protocoltypes.GroupTypeContact) + require.Equal(t, len(group.PublicKey), 32) + require.Equal(t, len(group.Secret), 32) +} + +func TestGetKeysForGroupOfContact(t *testing.T) { + privateKey, _, err := crypto.GenerateEd25519Key(crand.Reader) + require.NoError(t, err) + + groupPrivateKey, groupSecretPrivateKey, err := getKeysForGroupOfContact(privateKey) + require.NoError(t, err) + + require.NotNil(t, groupPrivateKey) + require.NotNil(t, groupSecretPrivateKey) + require.False(t, groupPrivateKey.Equals(groupSecretPrivateKey)) +} diff --git a/scenario_test.go b/scenario_test.go index 372b946a..9abe0c6f 100644 --- a/scenario_test.go +++ b/scenario_test.go @@ -890,12 +890,13 @@ func sendMessageOnGroup(ctx context.Context, t *testing.T, senders, receivers [] func getAccountPubKey(t *testing.T, tp *weshnet.TestingProtocol) []byte { t.Helper() - tpSK, err := tp.Opts.DeviceKeystore.AccountPrivKey() + _, accMemberDevice, err := tp.Opts.SecretStore.GetGroupForAccount() require.NoError(t, err) - tpPK, err := tpSK.GetPublic().Raw() + + publicKeyBytes, err := accMemberDevice.Member().Raw() require.NoError(t, err) - return tpPK + return publicKeyBytes } func getAccountB64PubKey(t *testing.T, tp *weshnet.TestingProtocol) string { diff --git a/service.go b/service.go index d934bf84..c86c9169 100644 --- a/service.go +++ b/service.go @@ -28,13 +28,13 @@ import ( "berty.tech/go-orbit-db/pubsub/directchannel" "berty.tech/weshnet/internal/bertyversion" "berty.tech/weshnet/internal/datastoreutil" - "berty.tech/weshnet/pkg/bertypush" "berty.tech/weshnet/pkg/bertyvcissuer" "berty.tech/weshnet/pkg/cryptoutil" "berty.tech/weshnet/pkg/errcode" "berty.tech/weshnet/pkg/ipfsutil" ipfs_mobile "berty.tech/weshnet/pkg/ipfsutil/mobile" "berty.tech/weshnet/pkg/protocoltypes" + "berty.tech/weshnet/pkg/secretstore" tinder "berty.tech/weshnet/pkg/tinder" "berty.tech/weshnet/pkg/tyber" ) @@ -57,18 +57,13 @@ type service struct { logger *zap.Logger ipfsCoreAPI ipfsutil.ExtendedCoreAPI odb *WeshOrbitDB - accountGroup *GroupContext - deviceKeystore cryptoutil.DeviceKeystore + accountGroupCtx *GroupContext openedGroups map[string]*GroupContext lock sync.RWMutex authSession atomic.Value close func() error startedAt time.Time host host.Host - groupDatastore *cryptoutil.GroupDatastore - pushHandler bertypush.PushHandler - accountCache ds.Batching - messageKeystore *cryptoutil.MessageKeystore pushClients map[string]*grpc.ClientConn muPushClients sync.RWMutex grpcInsecure bool @@ -79,28 +74,28 @@ type service struct { accountEventBus event.Bus contactRequestsManager *contactRequestsManager vcClient *bertyvcissuer.Client + secretStore secretstore.SecretStore protocoltypes.UnimplementedProtocolServiceServer } // Opts contains optional configuration flags for building a new Client type Opts struct { - Logger *zap.Logger - IpfsCoreAPI ipfsutil.ExtendedCoreAPI - DeviceKeystore cryptoutil.DeviceKeystore - DatastoreDir string - RootDatastore ds.Batching - GroupDatastore *cryptoutil.GroupDatastore - AccountCache ds.Batching - MessageKeystore *cryptoutil.MessageKeystore - OrbitDB *WeshOrbitDB - TinderService *tinder.Service - Host host.Host - PubSub *pubsub.PubSub - GRPCInsecureMode bool - LocalOnly bool - close func() error - PushKey *[cryptoutil.KeySize]byte + Logger *zap.Logger + IpfsCoreAPI ipfsutil.ExtendedCoreAPI + DatastoreDir string + RootDatastore ds.Batching + AccountCache ds.Batching + OrbitDB *WeshOrbitDB + TinderService *tinder.Service + Host host.Host + PubSub *pubsub.PubSub + GRPCInsecureMode bool + LocalOnly bool + close func() error + PushKey *[cryptoutil.KeySize]byte + SecretStore secretstore.SecretStore + OutOfStorePrivateKey *[cryptoutil.KeySize]byte // These are used if OrbitDB is nil. GroupMetadataStoreType string @@ -114,22 +109,6 @@ func (opts *Opts) applyPushDefaults() error { opts.applyDefaultsGetDatastore() - if opts.GroupDatastore == nil { - var err error - opts.GroupDatastore, err = cryptoutil.NewGroupDatastore(opts.RootDatastore) - if err != nil { - return err - } - } - - if opts.AccountCache == nil { - opts.AccountCache = datastoreutil.NewNamespacedDatastore(opts.RootDatastore, ds.NewKey(datastoreutil.NamespaceAccountCacheDatastore)) - } - - if opts.MessageKeystore == nil { - opts.MessageKeystore = cryptoutil.NewMessageKeystore(datastoreutil.NewNamespacedDatastore(opts.RootDatastore, ds.NewKey(datastoreutil.NamespaceMessageKeystore)), opts.Logger) - } - return nil } @@ -154,9 +133,16 @@ func (opts *Opts) applyDefaults(ctx context.Context) error { return err } - if opts.DeviceKeystore == nil { - ks := ipfsutil.NewDatastoreKeystore(datastoreutil.NewNamespacedDatastore(opts.RootDatastore, ds.NewKey(NamespaceDeviceKeystore))) - opts.DeviceKeystore = cryptoutil.NewDeviceKeystore(ks, nil) + if opts.SecretStore == nil { + secretStore, err := secretstore.NewSecretStore(opts.RootDatastore, &secretstore.NewSecretStoreOptions{ + Logger: opts.Logger, + OutOfStorePrivateKey: opts.OutOfStorePrivateKey, + }) + if err != nil { + return errcode.ErrInternal.Wrap(err) + } + + opts.SecretStore = secretStore } if opts.IpfsCoreAPI == nil { @@ -207,9 +193,8 @@ func (opts *Opts) applyDefaults(ctx context.Context) error { Directory: &orbitDirectory, Logger: opts.Logger, }, - Datastore: datastoreutil.NewNamespacedDatastore(opts.RootDatastore, ds.NewKey(NamespaceOrbitDBDatastore)), - DeviceKeystore: opts.DeviceKeystore, - + Datastore: datastoreutil.NewNamespacedDatastore(opts.RootDatastore, ds.NewKey(NamespaceOrbitDBDatastore)), + SecretStore: opts.SecretStore, GroupMetadataStoreType: opts.GroupMetadataStoreType, GroupMessageStoreType: opts.GroupMessageStoreType, } @@ -258,13 +243,13 @@ func NewService(opts Opts) (_ Service, err error) { LocalOnly: &opts.LocalOnly, } - acc, err := opts.OrbitDB.openAccountGroup(ctx, dbOpts, opts.IpfsCoreAPI) + accountGroupCtx, err := opts.OrbitDB.openAccountGroup(ctx, dbOpts, opts.IpfsCoreAPI) if err != nil { cancel() return nil, errcode.TODO.Wrap(err) } - opts.Logger.Debug("Opened account group", tyber.FormatStepLogFields(ctx, []tyber.Detail{{Name: "AccountGroup", Description: acc.group.String()}})...) + opts.Logger.Debug("Opened account group", tyber.FormatStepLogFields(ctx, []tyber.Detail{{Name: "AccountGroup", Description: accountGroupCtx.group.String()}})...) var contactRequestsManager *contactRequestsManager var swiper *Swiper @@ -272,7 +257,7 @@ func NewService(opts Opts) (_ Service, err error) { swiper = NewSwiper(opts.Logger, opts.TinderService, opts.OrbitDB.rotationInterval) opts.Logger.Debug("Tinder swiper is enabled", tyber.FormatStepLogFields(ctx, []tyber.Detail{})...) - if contactRequestsManager, err = newContactRequestsManager(swiper, acc.metadataStore, opts.IpfsCoreAPI, opts.Logger); err != nil { + if contactRequestsManager, err = newContactRequestsManager(swiper, accountGroupCtx.metadataStore, opts.IpfsCoreAPI, opts.Logger); err != nil { cancel() return nil, errcode.TODO.Wrap(err) } @@ -280,43 +265,26 @@ func NewService(opts Opts) (_ Service, err error) { opts.Logger.Warn("No tinder driver provided, incoming and outgoing contact requests won't be enabled", tyber.FormatStepLogFields(ctx, []tyber.Detail{})...) } - if err := opts.GroupDatastore.Put(ctx, acc.Group()); err != nil { + if err := opts.SecretStore.PutGroup(ctx, accountGroupCtx.Group()); err != nil { cancel() return nil, errcode.ErrInternal.Wrap(fmt.Errorf("unable to add account group to group datastore, err: %w", err)) } - pushHandler := (bertypush.PushHandler)(nil) - if opts.PushKey != nil { - pushHandler, err = bertypush.NewPushHandler(&bertypush.PushHandlerOpts{ - RootDatastore: opts.RootDatastore, - PushKey: opts.PushKey, - Logger: opts.Logger, - }) - if err != nil { - cancel() - return nil, errcode.ErrInternal.Wrap(fmt.Errorf("unable to init push handler: %w", err)) - } - } - s := &service{ - ctx: ctx, - ctxCancel: cancel, - host: opts.Host, - ipfsCoreAPI: opts.IpfsCoreAPI, - logger: opts.Logger, - odb: opts.OrbitDB, - deviceKeystore: opts.DeviceKeystore, - close: opts.close, - accountGroup: acc, - swiper: swiper, - startedAt: time.Now(), - groupDatastore: opts.GroupDatastore, + ctx: ctx, + ctxCancel: cancel, + host: opts.Host, + ipfsCoreAPI: opts.IpfsCoreAPI, + logger: opts.Logger, + odb: opts.OrbitDB, + close: opts.close, + accountGroupCtx: accountGroupCtx, + swiper: swiper, + startedAt: time.Now(), openedGroups: map[string]*GroupContext{ - string(acc.Group().PublicKey): acc, + string(accountGroupCtx.Group().PublicKey): accountGroupCtx, }, - accountCache: opts.AccountCache, - messageKeystore: opts.MessageKeystore, - pushHandler: pushHandler, + secretStore: opts.SecretStore, pushClients: make(map[string]*grpc.ClientConn), grpcInsecure: opts.GRPCInsecureMode, refreshprocess: make(map[string]context.CancelFunc), @@ -349,7 +317,7 @@ func (s *service) Close() error { } for _, gc := range s.openedGroups { - pk, subErr := crypto.UnmarshalEd25519PublicKey(gc.group.PublicKey) + pk, subErr := gc.group.GetPubKey() if subErr != nil { err = multierr.Append(err, subErr) continue diff --git a/service_group.go b/service_group.go index adf2ea76..b6d2ccef 100644 --- a/service_group.go +++ b/service_group.go @@ -8,29 +8,24 @@ import ( "go.uber.org/zap" "berty.tech/go-orbit-db/iface" - "berty.tech/weshnet/pkg/cryptoutil" "berty.tech/weshnet/pkg/errcode" "berty.tech/weshnet/pkg/protocoltypes" + "berty.tech/weshnet/pkg/secretstore" ) func (s *service) getContactGroup(key crypto.PubKey) (*protocoltypes.Group, error) { - sk, err := s.deviceKeystore.ContactGroupPrivKey(key) - if err != nil { - return nil, errcode.ErrCryptoKeyGeneration.Wrap(err) - } - - g, err := cryptoutil.GetGroupForContact(sk) + group, err := s.secretStore.GetGroupForContact(key) if err != nil { return nil, errcode.ErrOrbitDBOpen.Wrap(err) } - return g, nil + return group, nil } func (s *service) getGroupForPK(ctx context.Context, pk crypto.PubKey) (*protocoltypes.Group, error) { - g, err := s.groupDatastore.Get(ctx, pk) + group, err := s.secretStore.FetchGroupByPublicKey(ctx, pk) if err == nil { - return g, nil + return group, nil } else if !errcode.Is(err, errcode.ErrMissingMapKey) { return nil, errcode.ErrInternal.Wrap(err) } @@ -40,13 +35,13 @@ func (s *service) getGroupForPK(ctx context.Context, pk crypto.PubKey) (*protoco return nil, errcode.ErrGroupMissing } - if err = reindexGroupDatastore(ctx, s.groupDatastore, accountGroup.metadataStore, s.deviceKeystore); err != nil { + if err = reindexGroupDatastore(ctx, s.secretStore, accountGroup.metadataStore); err != nil { return nil, errcode.TODO.Wrap(err) } - g, err = s.groupDatastore.Get(ctx, pk) + group, err = s.secretStore.FetchGroupByPublicKey(ctx, pk) if err == nil { - return g, nil + return group, nil } else if errcode.Is(err, errcode.ErrMissingMapKey) { return nil, errcode.ErrInvalidInput.Wrap(fmt.Errorf("unknown group specified")) } @@ -77,7 +72,7 @@ func (s *service) deactivateGroup(pk crypto.PubKey) error { delete(s.openedGroups, string(id)) if cg.group.GroupType == protocoltypes.GroupTypeAccount { - s.accountGroup = nil + s.accountGroupCtx = nil } return nil @@ -108,11 +103,11 @@ func (s *service) activateGroup(ctx context.Context, pk crypto.PubKey, localOnly // nothing to get here, simply continue, open and activate the group case protocoltypes.GroupTypeContact: - if s.accountGroup == nil { - return errcode.ErrGroupActivate.Wrap(fmt.Errorf("accountGroup is deactivated")) + if s.accountGroupCtx == nil { + return errcode.ErrGroupActivate.Wrap(fmt.Errorf("accountGroupCtx is deactivated")) } - contact := s.accountGroup.metadataStore.GetContactFromGroupPK(id) + contact := s.accountGroupCtx.metadataStore.GetContactFromGroupPK(id) if contact != nil { contactPK, err = contact.GetPubKey() if err != nil { @@ -121,16 +116,16 @@ func (s *service) activateGroup(ctx context.Context, pk crypto.PubKey, localOnly } case protocoltypes.GroupTypeAccount: localOnly = true - if s.accountGroup, err = s.odb.openAccountGroup(ctx, &iface.CreateDBOptions{EventBus: s.accountEventBus, LocalOnly: &localOnly}, s.ipfsCoreAPI); err != nil { + if s.accountGroupCtx, err = s.odb.openAccountGroup(ctx, &iface.CreateDBOptions{EventBus: s.accountEventBus, LocalOnly: &localOnly}, s.ipfsCoreAPI); err != nil { return err } - s.openedGroups[string(id)] = s.accountGroup + s.openedGroups[string(id)] = s.accountGroupCtx // reinitialize contactRequestsManager if s.contactRequestsManager != nil { s.contactRequestsManager.close() - if s.contactRequestsManager, err = newContactRequestsManager(s.swiper, s.accountGroup.metadataStore, s.ipfsCoreAPI, s.logger); err != nil { + if s.contactRequestsManager, err = newContactRequestsManager(s.swiper, s.accountGroupCtx.metadataStore, s.ipfsCoreAPI, s.logger); err != nil { return errcode.TODO.Wrap(err) } } @@ -173,13 +168,13 @@ func (s *service) GetContextGroupForID(id []byte) (*GroupContext, error) { return nil, errcode.ErrGroupUnknown } -func reindexGroupDatastore(ctx context.Context, gd *cryptoutil.GroupDatastore, m *MetadataStore, deviceKeystore cryptoutil.DeviceKeystore) error { - if deviceKeystore == nil { +func reindexGroupDatastore(ctx context.Context, secretStore secretstore.SecretStore, m *MetadataStore) error { + if secretStore == nil { return errcode.ErrInvalidInput.Wrap(fmt.Errorf("missing device keystore")) } for _, g := range m.ListMultiMemberGroups() { - if err := gd.Put(ctx, g); err != nil { + if err := secretStore.PutGroup(ctx, g); err != nil { return errcode.ErrInternal.Wrap(err) } } @@ -197,7 +192,12 @@ func reindexGroupDatastore(ctx context.Context, gd *cryptoutil.GroupDatastore, m return errcode.TODO.Wrap(err) } - if err := gd.PutForContactPK(ctx, cPK, deviceKeystore); err != nil { + group, err := secretStore.GetGroupForContact(cPK) + if err != nil { + return errcode.ErrInternal.Wrap(err) + } + + if err := secretStore.PutGroup(ctx, group); err != nil { return errcode.ErrInternal.Wrap(err) } } @@ -208,5 +208,5 @@ func reindexGroupDatastore(ctx context.Context, gd *cryptoutil.GroupDatastore, m func (s *service) getAccountGroup() *GroupContext { s.lock.Lock() defer s.lock.Unlock() - return s.accountGroup + return s.accountGroupCtx } diff --git a/store_message.go b/store_message.go index f7bc7738..1d8935b5 100644 --- a/store_message.go +++ b/store_message.go @@ -13,7 +13,6 @@ import ( "github.com/libp2p/go-libp2p/core/event" "github.com/libp2p/go-libp2p/p2p/host/eventbus" "go.uber.org/zap" - "golang.org/x/crypto/nacl/secretbox" "golang.org/x/sync/semaphore" ipfslog "berty.tech/go-ipfs-log" @@ -24,11 +23,11 @@ import ( "berty.tech/go-orbit-db/stores" "berty.tech/go-orbit-db/stores/basestore" "berty.tech/go-orbit-db/stores/operation" - "berty.tech/weshnet/pkg/cryptoutil" "berty.tech/weshnet/pkg/errcode" "berty.tech/weshnet/pkg/logutil" "berty.tech/weshnet/pkg/protocoltypes" "berty.tech/weshnet/pkg/pushtypes" + "berty.tech/weshnet/pkg/secretstore" "berty.tech/weshnet/pkg/tyber" ) @@ -41,10 +40,12 @@ type MessageStore struct { groupCacheMessage event.Emitter } - devKS cryptoutil.DeviceKeystore - mks *cryptoutil.MessageKeystore - g *protocoltypes.Group - logger *zap.Logger + secretStore secretstore.SecretStore + currentDevicePublicKey crypto.PubKey + currentDevicePublicKeyRaw []byte + group *protocoltypes.Group + groupPublicKey crypto.PubKey + logger *zap.Logger deviceCaches map[string]*groupCache muDeviceCaches sync.RWMutex @@ -60,7 +61,7 @@ func (m *MessageStore) setLogger(l *zap.Logger) { return } - m.logger = l.With(logutil.PrivateString("group-id", fmt.Sprintf("%.6s", base64.StdEncoding.EncodeToString(m.g.PublicKey)))) + m.logger = l.With(logutil.PrivateString("group-id", fmt.Sprintf("%.6s", base64.StdEncoding.EncodeToString(m.group.PublicKey)))) } func (m *MessageStore) openMessage(ctx context.Context, e ipfslog.Entry) (*protocoltypes.GroupMessageEvent, error) { @@ -74,18 +75,17 @@ func (m *MessageStore) openMessage(ctx context.Context, e ipfslog.Entry) (*proto return nil, err } - ownPK := crypto.PubKey(nil) - md, inErr := m.devKS.MemberDeviceForGroup(m.g) - if inErr == nil { - ownPK = md.PrivateDevice().GetPublic() + env, headers, err := m.secretStore.OpenEnvelopeHeaders(op.GetValue(), m.group) + if err != nil { + return nil, errcode.ErrCryptoDecrypt.Wrap(err) } - env, headers, err := cryptoutil.OpenEnvelopeHeaders(op.GetValue(), m.g) + devicePublicKey, err := crypto.UnmarshalEd25519PublicKey(headers.DevicePK) if err != nil { - return nil, errcode.ErrCryptoDecrypt.Wrap(err) + return nil, errcode.ErrDeserialization.Wrap(err) } - if !m.mks.HasSecretForRawDevicePK(ctx, m.g.PublicKey, headers.DevicePK) { + if !m.secretStore.IsChainKeyKnownForDevice(ctx, m.groupPublicKey, devicePublicKey) { if err := m.addToMessageQueue(ctx, e); err != nil { m.logger.Error("unable to add message to cache", zap.Error(err)) } @@ -93,19 +93,18 @@ func (m *MessageStore) openMessage(ctx context.Context, e ipfslog.Entry) (*proto return nil, fmt.Errorf("no secret for device") } - return m.processMessage(ctx, ownPK, &messageItem{ + return m.processMessage(ctx, &messageItem{ op: op, env: env, headers: headers, - ownPK: ownPK, hash: e.GetHash(), }) } type groupCache struct { - self, hasSecret bool - locker sync.Locker - queue *priorityMessageQueue + self, hasKnownChainKey bool + locker sync.Locker + queue *priorityMessageQueue } func (m *MessageStore) CacheSizeForDevicePK(devicePK []byte) (size int, ok bool) { @@ -121,7 +120,11 @@ func (m *MessageStore) CacheSizeForDevicePK(devicePK []byte) (size int, ok bool) func (m *MessageStore) ProcessMessageQueueForDevicePK(ctx context.Context, devicePK []byte) { m.muDeviceCaches.Lock() if device, ok := m.deviceCaches[string(devicePK)]; ok { - if device.hasSecret = m.mks.HasSecretForRawDevicePK(ctx, m.g.PublicKey, devicePK); !device.hasSecret { + devicePublicKey, errDevice := crypto.UnmarshalEd25519PublicKey(devicePK) + + if errDevice != nil { + m.logger.Error("unable to process message, unmarshal of device pk failed", logutil.PrivateBinary("devicepk", devicePK)) + } else if device.hasKnownChainKey = m.secretStore.IsChainKeyKnownForDevice(ctx, m.groupPublicKey, devicePublicKey); !device.hasKnownChainKey { m.logger.Error("unable to process message, no secret found for device pk", logutil.PrivateBinary("devicepk", devicePK)) } else if next := device.queue.Next(); next != nil { m.cmessage <- next @@ -130,20 +133,20 @@ func (m *MessageStore) ProcessMessageQueueForDevicePK(ctx context.Context, devic m.muDeviceCaches.Unlock() } -func (m *MessageStore) processMessage(ctx context.Context, ownPK crypto.PubKey, message *messageItem) (*protocoltypes.GroupMessageEvent, error) { +func (m *MessageStore) processMessage(ctx context.Context, message *messageItem) (*protocoltypes.GroupMessageEvent, error) { // process message - msg, err := m.mks.OpenEnvelopePayload(ctx, message.env, message.headers, m.g, ownPK, message.hash) + msg, err := m.secretStore.OpenEnvelopePayload(ctx, message.env, message.headers, m.groupPublicKey, m.currentDevicePublicKey, message.hash) if err != nil { return nil, err } - err = m.mks.UpdatePushGroupReferences(ctx, message.headers.DevicePK, message.headers.Counter, m.g) + err = m.secretStore.UpdateOutOfStoreGroupReferences(ctx, message.headers.DevicePK, message.headers.Counter, m.group) if err != nil { m.logger.Error("unable to update push group references", zap.Error(err)) } entry := message.op.GetEntry() - eventContext := newEventContext(entry.GetHash(), entry.GetNext(), m.g) + eventContext := newEventContext(entry.GetHash(), entry.GetNext(), m.group) return &protocoltypes.GroupMessageEvent{ EventContext: eventContext, Headers: message.headers, @@ -152,14 +155,6 @@ func (m *MessageStore) processMessage(ctx context.Context, ownPK crypto.PubKey, } func (m *MessageStore) processMessageLoop(ctx context.Context) { - var ownPK crypto.PubKey - var rawOwnPK []byte - md, inErr := m.devKS.MemberDeviceForGroup(m.g) - if inErr == nil { - ownPK = md.PrivateDevice().GetPublic() - rawOwnPK, _ = ownPK.Raw() - } - semProcess := semaphore.NewWeighted(32) for { @@ -170,23 +165,29 @@ func (m *MessageStore) processMessageLoop(ctx context.Context) { return } - devicepk := string(message.headers.DevicePK) + devicePublicKeyString := string(message.headers.DevicePK) m.muDeviceCaches.Lock() - device, ok := m.deviceCaches[devicepk] + device, ok := m.deviceCaches[devicePublicKeyString] if !ok { - hasSecret := m.mks.HasSecretForRawDevicePK(ctx, m.g.PublicKey, message.headers.DevicePK) + devicePublicKey, err := crypto.UnmarshalEd25519PublicKey(message.headers.DevicePK) + if err != nil { + m.logger.Error("unable to process message, unmarshal of device pk failed", logutil.PrivateBinary("devicepk", message.headers.DevicePK)) + continue + } + + hasSecret := m.secretStore.IsChainKeyKnownForDevice(ctx, m.groupPublicKey, devicePublicKey) device = &groupCache{ - self: bytes.Equal(rawOwnPK, message.headers.DevicePK), - queue: newPriorityMessageQueue(), - locker: &sync.RWMutex{}, - hasSecret: hasSecret, + self: bytes.Equal(m.currentDevicePublicKeyRaw, message.headers.DevicePK), + queue: newPriorityMessageQueue(), + locker: &sync.RWMutex{}, + hasKnownChainKey: hasSecret, } - m.deviceCaches[devicepk] = device + m.deviceCaches[devicePublicKeyString] = device } - // check for device secret, if unavailable add message to cache queue - if !device.hasSecret { + // check for device chain key, if unavailable add message to cache queue + if !device.hasKnownChainKey { device.queue.Add(message) _ = m.emitters.groupCacheMessage.Emit(*message) m.muDeviceCaches.Unlock() @@ -205,7 +206,7 @@ func (m *MessageStore) processMessageLoop(ctx context.Context) { defer semProcess.Release(1) // process the message - evt, err := m.processMessage(ctx, ownPK, message) + evt, err := m.processMessage(ctx, message) if err != nil { if errcode.Is(err, errcode.ErrCryptoDecryptPayload) { // @FIXME(gfanton): this should not happen @@ -245,13 +246,7 @@ func (m *MessageStore) addToMessageQueue(ctx context.Context, e ipfslog.Entry) e return err } - ownPK := crypto.PubKey(nil) - md, inErr := m.devKS.MemberDeviceForGroup(m.g) - if inErr == nil { - ownPK = md.PrivateDevice().GetPublic() - } - - env, headers, err := cryptoutil.OpenEnvelopeHeaders(op.GetValue(), m.g) + env, headers, err := m.secretStore.OpenEnvelopeHeaders(op.GetValue(), m.group) if err != nil { return errcode.ErrCryptoDecrypt.Wrap(err) } @@ -260,7 +255,6 @@ func (m *MessageStore) addToMessageQueue(ctx context.Context, e ipfslog.Entry) e hash: e.GetHash(), env: env, headers: headers, - ownPK: ownPK, op: op, } @@ -305,7 +299,7 @@ func (m *MessageStore) AddMessage(ctx context.Context, payload []byte) (operatio ctx, newTrace := tyber.ContextWithTraceID(ctx) if newTrace { - m.logger.Debug("Sending message to group "+base64.RawURLEncoding.EncodeToString(m.g.PublicKey), tyber.FormatTraceLogFields(ctx)...) + m.logger.Debug("Sending message to group "+base64.RawURLEncoding.EncodeToString(m.group.PublicKey), tyber.FormatTraceLogFields(ctx)...) } m.logger.Debug( @@ -318,15 +312,10 @@ func (m *MessageStore) AddMessage(ctx context.Context, payload []byte) (operatio )..., ) - md, err := m.devKS.MemberDeviceForGroup(m.g) - if err != nil { - return nil, errcode.ErrInternal.Wrap(err) - } - - return messageStoreAddMessage(ctx, m.g, md, m, payload) + return messageStoreAddMessage(ctx, m.group, m, payload) } -func messageStoreAddMessage(ctx context.Context, g *protocoltypes.Group, md *cryptoutil.OwnMemberDevice, m *MessageStore, payload []byte) (operation.Operation, error) { +func messageStoreAddMessage(ctx context.Context, g *protocoltypes.Group, m *MessageStore, payload []byte) (operation.Operation, error) { msg, err := (&protocoltypes.EncryptedMessage{ Plaintext: payload, ProtocolMetadata: &protocoltypes.ProtocolMetadata{}, @@ -335,7 +324,7 @@ func messageStoreAddMessage(ctx context.Context, g *protocoltypes.Group, md *cry return nil, errcode.ErrInternal.Wrap(err) } - env, err := m.mks.SealEnvelope(ctx, g, md.PrivateDevice(), msg) + sealedEnvelope, err := m.secretStore.SealEnvelope(ctx, g, msg) if err != nil { return nil, errcode.ErrCryptoEncrypt.Wrap(err) } @@ -345,12 +334,12 @@ func messageStoreAddMessage(ctx context.Context, g *protocoltypes.Group, md *cry ctx, []tyber.Detail{ {Name: "Cleartext size", Description: fmt.Sprintf("%d bytes", len(msg))}, - {Name: "Cyphertext size", Description: fmt.Sprintf("%d bytes", len(env))}, + {Name: "Cyphertext size", Description: fmt.Sprintf("%d bytes", len(sealedEnvelope))}, }, )..., ) - op := operation.NewOperation(nil, "ADD", env) + op := operation.NewOperation(nil, "ADD", sealedEnvelope) e, err := m.AddOperation(ctx, op, nil) if err != nil { @@ -381,56 +370,69 @@ func constructorFactoryGroupMessage(s *WeshOrbitDB, logger *zap.Logger) iface.St return nil, errcode.ErrInvalidInput.Wrap(err) } + groupPublicKey, err := g.GetPubKey() + if err != nil { + return nil, errcode.ErrDeserialization.Wrap(err) + } + if options.EventBus == nil { options.EventBus = eventbus.NewBus() } replication := false - if s.deviceKeystore == nil { + store := &MessageStore{ + eventBus: options.EventBus, + secretStore: s.secretStore, + cmessage: make(chan *messageItem), + group: g, + groupPublicKey: groupPublicKey, + logger: logger, + deviceCaches: make(map[string]*groupCache), + } + + if s.replicationMode { replication = true } else { - if _, err := s.deviceKeystore.MemberDeviceForGroup(g); errcode.Is(err, errcode.ErrInvalidInput) { - replication = true - } else if err != nil { - return nil, errcode.ErrOrbitDBInit.Wrap(err) + currentMemberDevice, err := s.secretStore.GetOwnMemberDeviceForGroup(g) + + if err != nil { + if errcode.Is(err, errcode.ErrInvalidInput) { + replication = true + } else { + return nil, errcode.ErrOrbitDBInit.Wrap(err) + } + } else { + store.currentDevicePublicKey = currentMemberDevice.Device() + store.currentDevicePublicKeyRaw, err = store.currentDevicePublicKey.Raw() + if err != nil { + return nil, errcode.ErrOrbitDBInit.Wrap(err) + } } } - ctx, cancel := context.WithCancel(context.Background()) - - store := &MessageStore{ - ctx: ctx, - cancel: cancel, - eventBus: options.EventBus, - devKS: s.deviceKeystore, - mks: s.messageKeystore, - cmessage: make(chan *messageItem), - g: g, - logger: logger, - deviceCaches: make(map[string]*groupCache), - } + store.ctx, store.cancel = context.WithCancel(context.Background()) go func() { - store.processMessageLoop(ctx) - logger.Debug("store message process loop ended", zap.Error(ctx.Err())) + store.processMessageLoop(store.ctx) + logger.Debug("store message process loop ended", zap.Error(store.ctx.Err())) }() if store.emitters.groupMessage, err = store.eventBus.Emitter(new(protocoltypes.GroupMessageEvent)); err != nil { - cancel() + store.cancel() return nil, errcode.ErrOrbitDBInit.Wrap(err) } // for debug/test purpose if store.emitters.groupCacheMessage, err = store.eventBus.Emitter(new(messageItem)); err != nil { - cancel() + store.cancel() return nil, errcode.ErrOrbitDBInit.Wrap(err) } options.Index = basestore.NewNoopIndex if err := store.InitBaseStore(ipfs, identity, addr, options); err != nil { - cancel() + store.cancel() return nil, errcode.ErrOrbitDBInit.Wrap(err) } @@ -479,87 +481,43 @@ func constructorFactoryGroupMessage(s *WeshOrbitDB, logger *zap.Logger) iface.St } } } - }(ctx) + }(store.ctx) return store, nil } } -func (m *MessageStore) GetMessageByCID(c cid.Cid) (*protocoltypes.MessageEnvelope, *protocoltypes.MessageHeaders, error) { +func (m *MessageStore) GetMessageByCID(c cid.Cid) (operation.Operation, error) { logEntry, ok := m.OpLog().Get(c) if !ok { - return nil, nil, errcode.ErrInvalidInput.Wrap(fmt.Errorf("unable to find message entry")) + return nil, errcode.ErrInvalidInput.Wrap(fmt.Errorf("unable to find message entry")) } op, err := operation.ParseOperation(logEntry) if err != nil { - return nil, nil, errcode.ErrDeserialization.Wrap(err) + return nil, errcode.ErrDeserialization.Wrap(err) } - env, headers, err := cryptoutil.OpenEnvelopeHeaders(op.GetValue(), m.g) - if err != nil { - return nil, nil, errcode.ErrDeserialization.Wrap(err) - } - - return env, headers, nil + return op, nil } func (m *MessageStore) GetOutOfStoreMessageEnvelope(ctx context.Context, c cid.Cid) (*pushtypes.OutOfStoreMessageEnvelope, error) { - env, headers, err := m.GetMessageByCID(c) + op, err := m.GetMessageByCID(c) if err != nil { return nil, errcode.ErrInvalidInput.Wrap(err) } - sealedMessageEnvelope, err := SealOutOfStoreMessageEnvelope(c, env, headers, m.g) - if err != nil { - return nil, errcode.ErrInternal.Wrap(err) - } - - return sealedMessageEnvelope, nil -} - -func SealOutOfStoreMessageEnvelope(id cid.Cid, env *protocoltypes.MessageEnvelope, headers *protocoltypes.MessageHeaders, g *protocoltypes.Group) (*pushtypes.OutOfStoreMessageEnvelope, error) { - oosMessage := &protocoltypes.OutOfStoreMessage{ - CID: id.Bytes(), - DevicePK: headers.DevicePK, - Counter: headers.Counter, - Sig: headers.Sig, - EncryptedPayload: env.Message, - Nonce: env.Nonce, - } - - data, err := oosMessage.Marshal() - if err != nil { - return nil, errcode.ErrSerialization.Wrap(err) - } - - nonce, err := cryptoutil.GenerateNonce() - if err != nil { - return nil, errcode.ErrCryptoNonceGeneration.Wrap(err) - } - - secret, err := cryptoutil.KeySliceToArray(g.Secret) + env, headers, err := m.secretStore.OpenEnvelopeHeaders(op.GetValue(), m.group) if err != nil { - return nil, errcode.ErrSerialization.Wrap(err) + return nil, errcode.ErrDeserialization.Wrap(err) } - encryptedData := secretbox.Seal(nil, data, nonce, secret) - - groupPushSecret, err := cryptoutil.GetGroupPushSecret(g) - if err != nil { - return nil, errcode.ErrCryptoKeyGeneration.Wrap(err) - } - - pushGroupRef, err := cryptoutil.CreatePushGroupReference(headers.DevicePK, headers.Counter, groupPushSecret) + sealedMessageEnvelope, err := m.secretStore.SealOutOfStoreMessageEnvelope(c, env, headers, m.group) if err != nil { - return nil, errcode.ErrCryptoKeyGeneration.Wrap(err) + return nil, errcode.ErrInternal.Wrap(err) } - return &pushtypes.OutOfStoreMessageEnvelope{ - Nonce: nonce[:], - Box: encryptedData, - GroupReference: pushGroupRef, - }, nil + return sealedMessageEnvelope, nil } func (m *MessageStore) Close() error { diff --git a/store_message_queue.go b/store_message_queue.go index 2308b97d..6e3b30fb 100644 --- a/store_message_queue.go +++ b/store_message_queue.go @@ -5,7 +5,6 @@ import ( "sync" "github.com/ipfs/go-cid" - "github.com/libp2p/go-libp2p/core/crypto" "berty.tech/go-orbit-db/stores/operation" "berty.tech/weshnet/pkg/protocoltypes" @@ -16,7 +15,6 @@ type messageItem struct { op operation.Operation env *protocoltypes.MessageEnvelope headers *protocoltypes.MessageHeaders - ownPK crypto.PubKey hash cid.Cid } diff --git a/store_message_test.go b/store_message_test.go index c78fd576..e3160033 100644 --- a/store_message_test.go +++ b/store_message_test.go @@ -42,11 +42,11 @@ func Test_AddMessage_ListMessages_manually_supplying_secrets(t *testing.T) { defer cleanup() dPK0 := peers[0].GC.DevicePubKey() - ds0, err := peers[0].MKS.GetDeviceSecret(ctx, peers[0].GC.Group(), peers[0].DevKS) + ds0For1, err := peers[0].SecretStore.GetShareableChainKey(ctx, peers[0].GC.Group(), peers[1].GC.MemberPubKey()) require.NoError(t, err) - require.NotNil(t, ds0) + require.NotNil(t, ds0For1) - err = peers[1].MKS.RegisterChainKey(ctx, peers[0].GC.Group(), dPK0, ds0, false) + err = peers[1].SecretStore.RegisterChainKey(ctx, peers[0].GC.Group(), dPK0, ds0For1) require.NoError(t, err) _, err = peers[0].GC.MessageStore().AddMessage(ctx, testMsg1) @@ -135,9 +135,9 @@ func Test_Add_Messages_To_Cache(t *testing.T) { dPK0 := peers[0].GC.DevicePubKey() dPK0Raw, err := dPK0.Raw() require.NoError(t, err) - ds0, err := peers[0].MKS.GetDeviceSecret(ctx, peers[0].GC.Group(), peers[0].DevKS) + ds0For1, err := peers[0].SecretStore.GetShareableChainKey(ctx, peers[0].GC.Group(), peers[1].GC.MemberPubKey()) require.NoError(t, err) - require.NotNil(t, ds0) + require.NotNil(t, ds0For1) cevent, err := peers[0].GC.MessageStore().EventBus().Subscribe( new(protocoltypes.GroupMessageEvent), eventbus.BufSize(entriesCount)) @@ -186,7 +186,7 @@ func Test_Add_Messages_To_Cache(t *testing.T) { require.True(t, ok) require.Equal(t, entriesCount, size) - err = peers[1].MKS.RegisterChainKey(ctx, peers[0].GC.Group(), dPK0, ds0, false) + err = peers[1].SecretStore.RegisterChainKey(ctx, peers[0].GC.Group(), dPK0, ds0For1) require.NoError(t, err) cevent, err = peers[1].GC.MessageStore().EventBus().Subscribe( diff --git a/store_metadata.go b/store_metadata.go index 5ed0af6f..eecd0b63 100644 --- a/store_metadata.go +++ b/store_metadata.go @@ -15,7 +15,6 @@ import ( "github.com/libp2p/go-libp2p/core/event" "github.com/libp2p/go-libp2p/p2p/host/eventbus" "go.uber.org/zap" - "golang.org/x/crypto/nacl/box" ipfslog "berty.tech/go-ipfs-log" "berty.tech/go-ipfs-log/identityprovider" @@ -25,10 +24,10 @@ import ( "berty.tech/go-orbit-db/stores" "berty.tech/go-orbit-db/stores/basestore" "berty.tech/go-orbit-db/stores/operation" - "berty.tech/weshnet/pkg/cryptoutil" "berty.tech/weshnet/pkg/errcode" "berty.tech/weshnet/pkg/logutil" "berty.tech/weshnet/pkg/protocoltypes" + "berty.tech/weshnet/pkg/secretstore" "berty.tech/weshnet/pkg/tyber" ) @@ -40,25 +39,26 @@ type MetadataStore struct { metadataReceived event.Emitter } - g *protocoltypes.Group - devKS cryptoutil.DeviceKeystore - mks *cryptoutil.MessageKeystore - logger *zap.Logger + group *protocoltypes.Group + memberDevice secretstore.OwnMemberDevice + devicePublicKeyRaw []byte + secretStore secretstore.SecretStore + logger *zap.Logger ctx context.Context cancel context.CancelFunc } func isMultiMemberGroup(m *MetadataStore) bool { - return m.g.GroupType == protocoltypes.GroupTypeMultiMember + return m.group.GroupType == protocoltypes.GroupTypeMultiMember } func isAccountGroup(m *MetadataStore) bool { - return m.g.GroupType == protocoltypes.GroupTypeAccount + return m.group.GroupType == protocoltypes.GroupTypeAccount } func isContactGroup(m *MetadataStore) bool { - return m.g.GroupType == protocoltypes.GroupTypeContact + return m.group.GroupType == protocoltypes.GroupTypeContact } func (m *MetadataStore) typeChecker(types ...func(m *MetadataStore) bool) bool { @@ -76,7 +76,7 @@ func (m *MetadataStore) setLogger(l *zap.Logger) { return } - // m.logger = l.Named("store").With(logutil.PrivateString("group-id", fmt.Sprintf("%.6s", base64.StdEncoding.EncodeToString(m.g.PublicKey)))) + // m.logger = l.Named("store").With(logutil.PrivateString("group-id", fmt.Sprintf("%.6s", base64.StdEncoding.EncodeToString(m.group.PublicKey)))) m.logger = l.Named("metastore") if index, ok := m.Index().(loggable); ok { @@ -105,7 +105,7 @@ func openMetadataEntry(log ipfslog.Log, e ipfslog.Entry, g *protocoltypes.Group) // not used // func (m *MetadataStore) openMetadataEntry(e ipfslog.Entry) (*protocoltypes.GroupMetadataEvent, proto.Message, error) { -// return openMetadataEntry(m.OpLog(), e, m.g, m.devKS) +// return openMetadataEntry(m.OpLog(), e, m.group, m.devKS) // } // FIXME: use iterator instead to reduce resource usage (require go-ipfs-log improvements) @@ -122,7 +122,7 @@ func (m *MetadataStore) ListEvents(ctx context.Context, since, until []byte, rev entries, reverse, func(entry ipliface.IPFSLogEntry) { - event, _, err := openMetadataEntry(m.OpLog(), entry, m.g) + event, _, err := openMetadataEntry(m.OpLog(), entry, m.group) if err != nil { m.logger.Error("unable to open metadata event", zap.Error(err)) } else { @@ -139,31 +139,31 @@ func (m *MetadataStore) ListEvents(ctx context.Context, since, until []byte, rev } func (m *MetadataStore) AddDeviceToGroup(ctx context.Context) (operation.Operation, error) { - md, err := m.devKS.MemberDeviceForGroup(m.g) + md, err := m.secretStore.GetOwnMemberDeviceForGroup(m.group) if err != nil { return nil, errcode.ErrInternal.Wrap(err) } - return MetadataStoreAddDeviceToGroup(ctx, m, m.g, md) + return MetadataStoreAddDeviceToGroup(ctx, m, m.group, md) } -func MetadataStoreAddDeviceToGroup(ctx context.Context, m *MetadataStore, g *protocoltypes.Group, md *cryptoutil.OwnMemberDevice) (operation.Operation, error) { - device, err := md.PrivateDevice().GetPublic().Raw() +func MetadataStoreAddDeviceToGroup(ctx context.Context, m *MetadataStore, g *protocoltypes.Group, md secretstore.OwnMemberDevice) (operation.Operation, error) { + device, err := md.Device().Raw() if err != nil { return nil, errcode.ErrSerialization.Wrap(err) } - member, err := md.PrivateMember().GetPublic().Raw() + member, err := md.Member().Raw() if err != nil { return nil, errcode.ErrSerialization.Wrap(err) } - k, err := m.GetMemberByDevice(md.PrivateDevice().GetPublic()) + k, err := m.GetMemberByDevice(md.Device()) if err == nil && k != nil { return nil, nil } - memberSig, err := md.PrivateMember().Sign(device) + memberSig, err := md.MemberSign(device) if err != nil { return nil, errcode.ErrCryptoSignature.Wrap(err) } @@ -174,7 +174,7 @@ func MetadataStoreAddDeviceToGroup(ctx context.Context, m *MetadataStore, g *pro MemberSig: memberSig, } - sig, err := signProto(event, md.PrivateDevice()) + sig, err := signProtoWithDevice(event, md) if err != nil { return nil, errcode.ErrCryptoSignature.Wrap(err) } @@ -185,11 +185,6 @@ func MetadataStoreAddDeviceToGroup(ctx context.Context, m *MetadataStore, g *pro } func (m *MetadataStore) SendSecret(ctx context.Context, memberPK crypto.PubKey) (operation.Operation, error) { - md, err := m.devKS.MemberDeviceForGroup(m.g) - if err != nil { - return nil, errcode.ErrInternal.Wrap(err) - } - ok, err := m.Index().(*metadataStoreIndex).areSecretsAlreadySent(memberPK) if err != nil { return nil, errcode.ErrInvalidInput.Wrap(err) @@ -203,21 +198,16 @@ func (m *MetadataStore) SendSecret(ctx context.Context, memberPK crypto.PubKey) m.logger.Warn("sending secret to an unknown group member") } - ds, err := m.mks.GetDeviceSecret(ctx, m.g, m.devKS) + encryptedSecret, err := m.secretStore.GetShareableChainKey(ctx, m.group, memberPK) if err != nil { - return nil, errcode.ErrInvalidInput.Wrap(err) + return nil, errcode.ErrCryptoEncrypt.Wrap(err) } - return MetadataStoreSendSecret(ctx, m, m.g, md, memberPK, ds) + return MetadataStoreSendSecret(ctx, m, m.group, m.memberDevice, memberPK, encryptedSecret) } -func MetadataStoreSendSecret(ctx context.Context, m *MetadataStore, g *protocoltypes.Group, md *cryptoutil.OwnMemberDevice, memberPK crypto.PubKey, ds *protocoltypes.DeviceSecret) (operation.Operation, error) { - payload, err := newSecretEntryPayload(md.PrivateDevice(), memberPK, ds, g) - if err != nil { - return nil, errcode.ErrInternal.Wrap(err) - } - - devicePKRaw, err := md.PrivateDevice().GetPublic().Raw() +func MetadataStoreSendSecret(ctx context.Context, m *MetadataStore, g *protocoltypes.Group, md secretstore.OwnMemberDevice, memberPK crypto.PubKey, encryptedSecret []byte) (operation.Operation, error) { + devicePKRaw, err := md.Device().Raw() if err != nil { return nil, errcode.ErrSerialization.Wrap(err) } @@ -227,18 +217,18 @@ func MetadataStoreSendSecret(ctx context.Context, m *MetadataStore, g *protocolt return nil, errcode.ErrSerialization.Wrap(err) } - event := &protocoltypes.GroupAddDeviceSecret{ + event := &protocoltypes.GroupAddDeviceChainKey{ DevicePK: devicePKRaw, DestMemberPK: memberPKRaw, - Payload: payload, + Payload: encryptedSecret, } - sig, err := signProto(event, md.PrivateDevice()) + sig, err := signProtoWithDevice(event, md) if err != nil { return nil, errcode.ErrCryptoSignature.Wrap(err) } - return metadataStoreAddEvent(ctx, m, g, protocoltypes.EventTypeGroupDeviceSecretAdded, event, sig) + return metadataStoreAddEvent(ctx, m, g, protocoltypes.EventTypeGroupDeviceChainKeyAdded, event, sig) } func (m *MetadataStore) ClaimGroupOwnership(ctx context.Context, groupSK crypto.PrivKey) (operation.Operation, error) { @@ -246,29 +236,33 @@ func (m *MetadataStore) ClaimGroupOwnership(ctx context.Context, groupSK crypto. return nil, errcode.ErrGroupInvalidType } - md, err := m.devKS.MemberDeviceForGroup(m.g) - if err != nil { - return nil, errcode.ErrInternal.Wrap(err) + event := &protocoltypes.MultiMemberInitialMember{ + MemberPK: m.devicePublicKeyRaw, } - memberPK, err := md.PrivateMember().GetPublic().Raw() + sig, err := signProtoWithPrivateKey(event, groupSK) if err != nil { - return nil, errcode.ErrSerialization.Wrap(err) + return nil, errcode.ErrCryptoSignature.Wrap(err) } - event := &protocoltypes.MultiMemberInitialMember{ - MemberPK: memberPK, + return metadataStoreAddEvent(ctx, m, m.group, protocoltypes.EventTypeMultiMemberGroupInitialMemberAnnounced, event, sig) +} + +func signProtoWithDevice(message proto.Message, memberDevice secretstore.OwnMemberDevice) ([]byte, error) { + data, err := proto.Marshal(message) + if err != nil { + return nil, errcode.ErrSerialization.Wrap(err) } - sig, err := signProto(event, groupSK) + sig, err := memberDevice.DeviceSign(data) if err != nil { return nil, errcode.ErrCryptoSignature.Wrap(err) } - return metadataStoreAddEvent(ctx, m, m.g, protocoltypes.EventTypeMultiMemberGroupInitialMemberAnnounced, event, sig) + return sig, nil } -func signProto(message proto.Message, sk crypto.PrivKey) ([]byte, error) { +func signProtoWithPrivateKey(message proto.Message, sk crypto.PrivKey) ([]byte, error) { data, err := proto.Marshal(message) if err != nil { return nil, errcode.ErrSerialization.Wrap(err) @@ -348,20 +342,14 @@ func (m *MetadataStore) GetIncomingContactRequestsStatus() (bool, *protocoltypes enabled := m.Index().(*metadataStoreIndex).contactRequestsEnabled() seed := m.Index().(*metadataStoreIndex).contactRequestsSeed() - md, err := m.devKS.MemberDeviceForGroup(m.g) - if err != nil { - m.logger.Error("unable to get member device for group", zap.Error(err)) - return enabled, nil - } - - pkBytes, err := md.PrivateMember().GetPublic().Raw() + rawMemberDevice, err := m.memberDevice.Member().Raw() if err != nil { m.logger.Error("unable to serialize member public key", zap.Error(err)) return enabled, nil } contactRef := &protocoltypes.ShareableContact{ - PK: pkBytes, + PK: rawMemberDevice, PublicRendezvousSeed: seed, } @@ -572,7 +560,7 @@ func (m *MetadataStore) ContactRequestReferenceReset(ctx context.Context) (opera func (m *MetadataStore) ContactRequestOutgoingEnqueue(ctx context.Context, contact *protocoltypes.ShareableContact, ownMetadata []byte) (operation.Operation, error) { ctx, _ = tyber.ContextWithTraceID(ctx) - b64GroupPK := base64.RawURLEncoding.EncodeToString(m.g.PublicKey) + b64GroupPK := base64.RawURLEncoding.EncodeToString(m.group.PublicKey) m.logger.Debug("Enqueuing contact request", tyber.FormatStepLogFields(ctx, []tyber.Detail{{Name: "GroupPK", Description: fmt.Sprint(b64GroupPK)}})...) if !m.typeChecker(isAccountGroup) { @@ -583,12 +571,8 @@ func (m *MetadataStore) ContactRequestOutgoingEnqueue(ctx context.Context, conta return nil, errcode.ErrInvalidInput.Wrap(err) } - accSK, err := m.devKS.AccountPrivKey() - if err != nil { - return nil, errcode.ErrInternal.Wrap(err) - } - - if contact.IsSamePK(accSK.GetPublic()) { + accountPublicKey := m.memberDevice.Member() + if contact.IsSamePK(accountPublicKey) { return nil, errcode.ErrContactRequestSameAccount } @@ -656,12 +640,8 @@ func (m *MetadataStore) ContactRequestIncomingReceived(ctx context.Context, cont return nil, errcode.ErrInvalidInput.Wrap(err) } - accSK, err := m.devKS.AccountPrivKey() - if err != nil { - return nil, errcode.ErrInternal.Wrap(err) - } - - if contact.IsSamePK(accSK.GetPublic()) { + accountPublicKey := m.memberDevice.Member() + if contact.IsSamePK(accountPublicKey) { return nil, errcode.ErrContactRequestSameAccount } @@ -730,12 +710,8 @@ func (m *MetadataStore) ContactBlock(ctx context.Context, pk crypto.PubKey) (ope return nil, errcode.ErrGroupInvalidType } - accSK, err := m.devKS.AccountPrivKey() - if err != nil { - return nil, errcode.ErrInternal.Wrap(err) - } - - if accSK.GetPublic().Equals(pk) { + accountPublicKey := m.memberDevice.Member() + if accountPublicKey.Equals(pk) { return nil, errcode.ErrInvalidInput } @@ -764,12 +740,12 @@ func (m *MetadataStore) ContactSendAliasKey(ctx context.Context) (operation.Oper return nil, errcode.ErrGroupInvalidType } - sk, err := m.devKS.AccountProofPrivKey() + accountProofPublicKey, err := m.secretStore.GetAccountProofPublicKey() if err != nil { return nil, errcode.ErrInternal.Wrap(err) } - alias, err := sk.GetPublic().Raw() + alias, err := accountProofPublicKey.Raw() if err != nil { return nil, errcode.ErrInternal.Wrap(err) } @@ -869,30 +845,16 @@ type accountGroupEvent interface { } func (m *MetadataStore) attributeSignAndAddEvent(ctx context.Context, evt accountSignableEvent, eventType protocoltypes.EventType) (operation.Operation, error) { - md, err := m.devKS.MemberDeviceForGroup(m.g) - if err != nil { - return nil, errcode.ErrInternal.Wrap(err) - } + evt.SetDevicePK(m.devicePublicKeyRaw) - m.logger.Debug("Got member device", tyber.FormatStepLogFields(ctx, []tyber.Detail{{Name: "MemberDevice", Description: fmt.Sprint(md)}})...) - - device, err := md.PrivateDevice().GetPublic().Raw() - if err != nil { - return nil, errcode.ErrSerialization.Wrap(err) - } - - m.logger.Debug("Got member device public key", tyber.FormatStepLogFields(ctx, []tyber.Detail{{Name: "MemberDevicePublicKey", Description: base64.RawURLEncoding.EncodeToString(device)}})...) - - evt.SetDevicePK(device) - - sig, err := signProto(evt, md.PrivateDevice()) + sig, err := signProtoWithDevice(evt, m.memberDevice) if err != nil { return nil, errcode.ErrCryptoSignature.Wrap(err) } m.logger.Debug("Signed event", tyber.FormatStepLogFields(ctx, []tyber.Detail{{Name: "Signature", Description: base64.RawURLEncoding.EncodeToString(sig)}})...) - return metadataStoreAddEvent(ctx, m, m.g, eventType, evt, sig) + return metadataStoreAddEvent(ctx, m, m.group, eventType, evt, sig) } func (m *MetadataStore) contactAction(ctx context.Context, pk crypto.PubKey, event accountContactEvent, evtType protocoltypes.EventType) (operation.Operation, error) { @@ -994,37 +956,40 @@ func constructorFactoryGroupMetadata(s *WeshOrbitDB, logger *zap.Logger) iface.S shortGroupType := strings.TrimPrefix(g.GetGroupType().String(), "GroupType") b64GroupPK := base64.RawURLEncoding.EncodeToString(g.PublicKey) - var ( - md *cryptoutil.OwnMemberDevice - replication = false - ) + replication := false if options.EventBus == nil { options.EventBus = eventbus.NewBus() } - if s.deviceKeystore == nil { + store := &MetadataStore{ + eventBus: options.EventBus, + group: g, + logger: logger, + secretStore: s.secretStore, + } + + if s.replicationMode { replication = true } else { - md, err = s.deviceKeystore.MemberDeviceForGroup(g) - if errcode.Is(err, errcode.ErrInvalidInput) { - replication = true - } else if err != nil { - return nil, errcode.TODO.Wrap(err) + var err error + + store.memberDevice, err = s.secretStore.GetOwnMemberDeviceForGroup(g) + if err != nil { + if errcode.Is(err, errcode.ErrInvalidInput) { + replication = true + } else { + return nil, errcode.ErrOrbitDBInit.Wrap(err) + } + } else { + store.devicePublicKeyRaw, err = store.memberDevice.Device().Raw() + if err != nil { + return nil, errcode.ErrOrbitDBInit.Wrap(err) + } } } - ctx, cancel := context.WithCancel(context.Background()) - - store := &MetadataStore{ - ctx: ctx, - cancel: cancel, - eventBus: options.EventBus, - g: g, - mks: s.messageKeystore, - devKS: s.deviceKeystore, - logger: logger, - } + store.ctx, store.cancel = context.WithCancel(context.Background()) if err := store.initEmitter(); err != nil { return nil, fmt.Errorf("unable to init emitters: %w", err) @@ -1033,7 +998,7 @@ func constructorFactoryGroupMetadata(s *WeshOrbitDB, logger *zap.Logger) iface.S if replication { options.Index = basestore.NewNoopIndex if err := store.InitBaseStore(ipfs, identity, addr, options); err != nil { - cancel() + store.cancel() return nil, errcode.ErrOrbitDBInit.Wrap(err) } @@ -1045,7 +1010,7 @@ func constructorFactoryGroupMetadata(s *WeshOrbitDB, logger *zap.Logger) iface.S new(stores.EventReplicated), }, eventbus.BufSize(128)) if err != nil { - cancel() + store.cancel() return nil, fmt.Errorf("unable to subscribe to store events") } @@ -1105,11 +1070,11 @@ func constructorFactoryGroupMetadata(s *WeshOrbitDB, logger *zap.Logger) iface.S } } } - }(ctx) + }(store.ctx) - options.Index = newMetadataIndex(ctx, g, md.Public(), s.deviceKeystore) + options.Index = newMetadataIndex(store.ctx, g, store.memberDevice, s.secretStore) if err := store.InitBaseStore(ipfs, identity, addr, options); err != nil { - cancel() + store.cancel() return nil, errcode.ErrOrbitDBInit.Wrap(err) } @@ -1117,23 +1082,6 @@ func constructorFactoryGroupMetadata(s *WeshOrbitDB, logger *zap.Logger) iface.S } } -func newSecretEntryPayload(localDevicePrivKey crypto.PrivKey, remoteMemberPubKey crypto.PubKey, secret *protocoltypes.DeviceSecret, group *protocoltypes.Group) ([]byte, error) { - message, err := secret.Marshal() - if err != nil { - return nil, errcode.ErrSerialization.Wrap(err) - } - - mongPriv, mongPub, err := cryptoutil.EdwardsToMontgomery(localDevicePrivKey, remoteMemberPubKey) - if err != nil { - return nil, errcode.ErrCryptoKeyConversion.Wrap(err) - } - - nonce := groupIDToNonce(group) - encryptedSecret := box.Seal(nil, message, nonce, mongPub, mongPriv) - - return encryptedSecret, nil -} - func (m *MetadataStore) SendPushToken(ctx context.Context, t *protocoltypes.PushMemberTokenUpdate) (operation.Operation, error) { m.logger.Debug("sending push token to device", logutil.PrivateString("server", t.Server.ServiceAddr)) return m.attributeSignAndAddEvent(ctx, &protocoltypes.PushMemberTokenUpdate{ @@ -1190,28 +1138,6 @@ func (m *MetadataStore) GetPushTokenForDevice(d crypto.PubKey) (*protocoltypes.P return token, nil } -func (m *MetadataStore) MemberPK() (crypto.PubKey, error) { - memDev, err := m.devKS.MemberDeviceForGroup(m.g) - if err != nil { - return nil, errcode.ErrInternal.Wrap(err) - } - - return memDev.PrivateMember().GetPublic(), nil -} - -func (m *MetadataStore) DevicePK() (crypto.PubKey, error) { - memDev, err := m.devKS.MemberDeviceForGroup(m.g) - if err != nil { - return nil, errcode.ErrInternal.Wrap(err) - } - - return memDev.PrivateDevice().GetPublic(), nil -} - -func (m *MetadataStore) Group() *protocoltypes.Group { - return m.g.Copy() -} - func (m *MetadataStore) initEmitter() (err error) { if m.emitters.metadataReceived, err = m.eventBus.Emitter(new(EventMetadataReceived)); err != nil { return diff --git a/store_metadata_index.go b/store_metadata_index.go index 60cab359..e6d887c5 100644 --- a/store_metadata_index.go +++ b/store_metadata_index.go @@ -15,12 +15,13 @@ import ( "berty.tech/weshnet/pkg/cryptoutil" "berty.tech/weshnet/pkg/errcode" "berty.tech/weshnet/pkg/protocoltypes" + "berty.tech/weshnet/pkg/secretstore" ) // FIXME: replace members, devices, sentSecrets, contacts and groups by a circular buffer to avoid an attack by RAM saturation type metadataStoreIndex struct { - members map[string][]*cryptoutil.MemberDevice - devices map[string]*cryptoutil.MemberDevice + members map[string][]secretstore.MemberDevice + devices map[string]secretstore.MemberDevice handledEvents map[string]struct{} sentSecrets map[string]struct{} admins map[crypto.PubKey]struct{} @@ -40,9 +41,9 @@ type metadataStoreIndex struct { eventsContactAddAliasKey []*protocoltypes.ContactAddAliasKey ownAliasKeySent bool otherAliasKey []byte - g *protocoltypes.Group - ownMemberDevice *cryptoutil.MemberDevice - deviceKeystore cryptoutil.DeviceKeystore + group *protocoltypes.Group + ownMemberDevice secretstore.MemberDevice + secretStore secretstore.SecretStore ctx context.Context lock sync.RWMutex logger *zap.Logger @@ -86,11 +87,11 @@ func (m *metadataStoreIndex) UpdateIndex(log ipfslog.Log, _ []ipfslog.Entry) err _, alreadyHandledEvent := m.handledEvents[e.GetHash().String()] // TODO: improve account events handling - if m.g.GroupType != protocoltypes.GroupTypeAccount && alreadyHandledEvent { + if m.group.GroupType != protocoltypes.GroupTypeAccount && alreadyHandledEvent { continue } - metaEvent, event, err := openMetadataEntry(log, e, m.g) + metaEvent, event, err := openMetadataEntry(log, e, m.group) if err != nil { m.logger.Error("unable to open metadata entry", zap.Error(err)) continue @@ -150,21 +151,16 @@ func (m *metadataStoreIndex) handleGroupAddMemberDevice(event proto.Message) err return nil } - m.devices[string(e.DevicePK)] = &cryptoutil.MemberDevice{ - Member: member, - Device: device, - } + memberDevice := secretstore.NewMemberDevice(member, device) - m.members[string(e.MemberPK)] = append(m.members[string(e.MemberPK)], &cryptoutil.MemberDevice{ - Member: member, - Device: device, - }) + m.devices[string(e.DevicePK)] = memberDevice + m.members[string(e.MemberPK)] = append(m.members[string(e.MemberPK)], memberDevice) return nil } -func (m *metadataStoreIndex) handleGroupAddDeviceSecret(event proto.Message) error { - e, ok := event.(*protocoltypes.GroupAddDeviceSecret) +func (m *metadataStoreIndex) handleGroupAddDeviceChainKey(event proto.Message) error { + e, ok := event.(*protocoltypes.GroupAddDeviceChainKey) if !ok { return errcode.ErrInvalidInput } @@ -179,32 +175,36 @@ func (m *metadataStoreIndex) handleGroupAddDeviceSecret(event proto.Message) err return errcode.ErrDeserialization.Wrap(err) } - if m.ownMemberDevice.Device.Equals(senderPK) { + if m.ownMemberDevice.Device().Equals(senderPK) { m.sentSecrets[string(e.DestMemberPK)] = struct{}{} } return nil } -func (m *metadataStoreIndex) getMemberByDevice(pk crypto.PubKey) (crypto.PubKey, error) { +func (m *metadataStoreIndex) getMemberByDevice(devicePublicKey crypto.PubKey) (crypto.PubKey, error) { m.lock.RLock() defer m.lock.RUnlock() - return m.unsafeGetMemberByDevice(pk) -} - -func (m *metadataStoreIndex) unsafeGetMemberByDevice(pk crypto.PubKey) (crypto.PubKey, error) { - id, err := pk.Raw() + publicKeyBytes, err := devicePublicKey.Raw() if err != nil { return nil, errcode.ErrInvalidInput.Wrap(err) } - device, ok := m.devices[string(id)] + return m.unsafeGetMemberByDevice(publicKeyBytes) +} + +func (m *metadataStoreIndex) unsafeGetMemberByDevice(publicKeyBytes []byte) (crypto.PubKey, error) { + if l := len(publicKeyBytes); l != cryptoutil.KeySize { + return nil, errcode.ErrInvalidInput.Wrap(fmt.Errorf("invalid private key size, expected %d got %d", cryptoutil.KeySize, l)) + } + + device, ok := m.devices[string(publicKeyBytes)] if !ok { return nil, errcode.ErrMissingInput } - return device.Member, nil + return device.Member(), nil } func (m *metadataStoreIndex) getDevicesForMember(pk crypto.PubKey) ([]crypto.PubKey, error) { @@ -223,7 +223,7 @@ func (m *metadataStoreIndex) getDevicesForMember(pk crypto.PubKey) ([]crypto.Pub ret := make([]crypto.PubKey, len(mds)) for i, md := range mds { - ret[i] = md.Device + ret[i] = md.Device() } return ret, nil @@ -278,7 +278,7 @@ func (m *metadataStoreIndex) listMembers() []crypto.PubKey { i := 0 for _, md := range m.members { - members[i] = md[0].Member + members[i] = md[0].Member() i++ } @@ -293,7 +293,7 @@ func (m *metadataStoreIndex) listDevices() []crypto.PubKey { i := 0 for _, md := range m.devices { - devices[i] = md.Device + devices[i] = md.Device() i++ } @@ -415,7 +415,7 @@ func (m *metadataStoreIndex) handleContactRequestReferenceReset(event proto.Mess } func (m *metadataStoreIndex) registerContactFromGroupPK(ac *AccountContact) error { - if m.g.GroupType != protocoltypes.GroupTypeAccount { + if m.group.GroupType != protocoltypes.GroupTypeAccount { return errcode.ErrGroupInvalidType } @@ -424,17 +424,12 @@ func (m *metadataStoreIndex) registerContactFromGroupPK(ac *AccountContact) erro return errcode.ErrDeserialization.Wrap(err) } - sk, err := m.deviceKeystore.ContactGroupPrivKey(contactPK) - if err != nil { - return err - } - - g, err := cryptoutil.GetGroupForContact(sk) + group, err := m.secretStore.GetGroupForContact(contactPK) if err != nil { return errcode.ErrOrbitDBOpen.Wrap(err) } - m.contactsFromGroupPK[string(g.PublicKey)] = ac + m.contactsFromGroupPK[string(group.PublicKey)] = ac return nil } @@ -725,7 +720,7 @@ func (m *metadataStoreIndex) handlePushDeviceTokenRegistered(event proto.Message return errcode.ErrInvalidInput } - devicePK, err := m.ownMemberDevice.Device.Raw() + devicePK, err := m.ownMemberDevice.Device().Raw() if err != nil { return errcode.ErrSerialization.Wrap(err) } @@ -753,7 +748,7 @@ func (m *metadataStoreIndex) handlePushServerTokenRegistered(event proto.Message return errcode.ErrInvalidInput } - devicePK, err := m.ownMemberDevice.Device.Raw() + devicePK, err := m.ownMemberDevice.Device().Raw() if err != nil { return errcode.ErrSerialization.Wrap(err) } @@ -801,11 +796,11 @@ func (m *metadataStoreIndex) listOtherMembersDevices() []crypto.PubKey { m.lock.RLock() defer m.lock.RUnlock() - if m.ownMemberDevice == nil || m.ownMemberDevice.Member == nil { + if m.ownMemberDevice == nil || m.ownMemberDevice.Member() == nil { return nil } - ownMemberPK, err := m.ownMemberDevice.Member.Raw() + ownMemberPK, err := m.ownMemberDevice.Member().Raw() if err != nil { m.logger.Warn("unable to serialize member pubkey", zap.Error(err)) return nil @@ -818,7 +813,7 @@ func (m *metadataStoreIndex) listOtherMembersDevices() []crypto.PubKey { } for _, md := range devicesForMember { - devices = append(devices, md.Device) + devices = append(devices, md.Device()) } } @@ -872,23 +867,18 @@ func (m *metadataStoreIndex) getCurrentDevicePushServer() *protocoltypes.PushDev func (m *metadataStoreIndex) postHandlerSentAliases() error { for _, evt := range m.eventsContactAddAliasKey { - pk, err := crypto.UnmarshalEd25519PublicKey(evt.DevicePK) - if err != nil { - return errcode.ErrDeserialization.Wrap(err) - } - - memberPK, err := m.unsafeGetMemberByDevice(pk) + memberPublicKey, err := m.unsafeGetMemberByDevice(evt.DevicePK) if err != nil { return fmt.Errorf("couldn't get member for device") } - if memberPK.Equals(m.ownMemberDevice.Member) { + if memberPublicKey.Equals(m.ownMemberDevice.Member()) { m.ownAliasKeySent = true continue } - if _, err = crypto.UnmarshalEd25519PublicKey(evt.AliasPK); err != nil { - return errcode.ErrDeserialization.Wrap(err) + if l := len(evt.AliasPK); l != cryptoutil.KeySize { + return errcode.ErrInvalidInput.Wrap(fmt.Errorf("invalid alias key size, expected %d, got %d", cryptoutil.KeySize, l)) } m.otherAliasKey = evt.AliasPK @@ -901,11 +891,11 @@ func (m *metadataStoreIndex) postHandlerSentAliases() error { // nolint:staticcheck // newMetadataIndex returns a new index to manage the list of the group members -func newMetadataIndex(ctx context.Context, g *protocoltypes.Group, md *cryptoutil.MemberDevice, devKS cryptoutil.DeviceKeystore) iface.IndexConstructor { +func newMetadataIndex(ctx context.Context, g *protocoltypes.Group, md secretstore.MemberDevice, secretStore secretstore.SecretStore) iface.IndexConstructor { return func(publicKey []byte) iface.StoreIndex { m := &metadataStoreIndex{ - members: map[string][]*cryptoutil.MemberDevice{}, - devices: map[string]*cryptoutil.MemberDevice{}, + members: map[string][]secretstore.MemberDevice{}, + devices: map[string]secretstore.MemberDevice{}, admins: map[crypto.PubKey]struct{}{}, sentSecrets: map[string]struct{}{}, handledEvents: map[string]struct{}{}, @@ -915,9 +905,9 @@ func newMetadataIndex(ctx context.Context, g *protocoltypes.Group, md *cryptouti serviceTokens: map[string]*protocoltypes.ServiceToken{}, contactRequestMetadata: map[string][]byte{}, membersPushTokens: map[string]*protocoltypes.PushMemberTokenUpdate{}, - g: g, + group: g, ownMemberDevice: md, - deviceKeystore: devKS, + secretStore: secretStore, ctx: ctx, logger: zap.NewNop(), } @@ -936,7 +926,7 @@ func newMetadataIndex(ctx context.Context, g *protocoltypes.Group, md *cryptouti protocoltypes.EventTypeAccountGroupJoined: {m.handleGroupJoined}, protocoltypes.EventTypeAccountGroupLeft: {m.handleGroupLeft}, protocoltypes.EventTypeContactAliasKeyAdded: {m.handleContactAliasKeyAdded}, - protocoltypes.EventTypeGroupDeviceSecretAdded: {m.handleGroupAddDeviceSecret}, + protocoltypes.EventTypeGroupDeviceChainKeyAdded: {m.handleGroupAddDeviceChainKey}, protocoltypes.EventTypeGroupMemberDeviceAdded: {m.handleGroupAddMemberDevice}, protocoltypes.EventTypeMultiMemberGroupAdminRoleGranted: {m.handleMultiMemberGrantAdminRole}, protocoltypes.EventTypeMultiMemberGroupInitialMemberAnnounced: {m.handleMultiMemberInitialMember}, diff --git a/store_metadata_test.go b/store_metadata_test.go index 82394813..55eaa673 100644 --- a/store_metadata_test.go +++ b/store_metadata_test.go @@ -17,7 +17,6 @@ import ( "github.com/stretchr/testify/require" "go.uber.org/zap" - "berty.tech/weshnet/pkg/cryptoutil" "berty.tech/weshnet/pkg/ipfsutil" "berty.tech/weshnet/pkg/protocoltypes" "berty.tech/weshnet/pkg/testutil" @@ -43,8 +42,8 @@ func TestMetadataStoreSecret_Basic(t *testing.T) { go peers[0].GC.WatchNewMembersAndSendSecrets() go peers[1].GC.WatchNewMembersAndSendSecrets() - go waitForBertyEventType(ctx, t, msA, protocoltypes.EventTypeGroupDeviceSecretAdded, 2, secretsAdded) - go waitForBertyEventType(ctx, t, msB, protocoltypes.EventTypeGroupDeviceSecretAdded, 2, secretsAdded) + go waitForBertyEventType(ctx, t, msA, protocoltypes.EventTypeGroupDeviceChainKeyAdded, 2, secretsAdded) + go waitForBertyEventType(ctx, t, msB, protocoltypes.EventTypeGroupDeviceChainKeyAdded, 2, secretsAdded) inviteAllPeersToGroup(ctx, t, peers, groupSK) devPkA := peers[0].GC.DevicePubKey() @@ -55,24 +54,6 @@ func TestMetadataStoreSecret_Basic(t *testing.T) { _ = devPkA _ = devPkB - - // secretAForB, err := msB.DeviceSecret(devPkA) - // assert.NoError(t, err) - // - // secretBForA, err := msA.DeviceSecret(devPkB) - // assert.NoError(t, err) - // - // secretAForA, err := peers[0].GetGroupContext().DeviceSecret(ctx) - // assert.NoError(t, err) - // - // secretBForB, err := peers[1].GetGroupContext().DeviceSecret(ctx) - // assert.NoError(t, err) - // - // assert.Equal(t, secretAForA.ChainKey, secretAForB.ChainKey) - // assert.Equal(t, secretAForA.Counter, secretAForB.Counter) - // - // assert.Equal(t, secretBForB.ChainKey, secretBForA.ChainKey) - // assert.Equal(t, secretBForB.Counter, secretBForA.Counter) } func TestMetadataStoreMember(t *testing.T) { @@ -193,9 +174,9 @@ func TestMetadataRendezvousPointLifecycle(t *testing.T) { meta := ownCG.MetadataStore() - accSK, err := peers[0].DevKS.AccountPrivKey() + _, accountMemberDevice, err := peers[0].SecretStore.GetGroupForAccount() assert.NoError(t, err) - accPK, err := accSK.GetPublic().Raw() + accPK, err := accountMemberDevice.Member().Raw() assert.NoError(t, err) enabled, shareableContact := meta.GetIncomingContactRequestsStatus() @@ -521,10 +502,7 @@ func TestMetadataAliasLifecycle(t *testing.T) { _, err := peers[0].GC.MetadataStore().ContactSendAliasKey(ctx) require.Error(t, err) - sk, err := peers[0].DevKS.ContactGroupPrivKey(peers[1].GC.MemberPubKey()) - require.NoError(t, err) - - g, err := cryptoutil.GetGroupForContact(sk) + g, err := peers[0].SecretStore.GetGroupForContact(peers[1].GC.MemberPubKey()) require.NoError(t, err) cg0, err := peers[0].DB.OpenGroup(ctx, g, nil) @@ -542,10 +520,7 @@ func TestMetadataAliasLifecycle(t *testing.T) { require.Empty(t, cg0.MetadataStore().Index().(*metadataStoreIndex).otherAliasKey) require.True(t, cg0.MetadataStore().Index().(*metadataStoreIndex).ownAliasKeySent) - sk, err = peers[1].DevKS.ContactGroupPrivKey(peers[0].GC.MemberPubKey()) - require.NoError(t, err) - - g, err = cryptoutil.GetGroupForContact(sk) + g, err = peers[1].SecretStore.GetGroupForContact(peers[0].GC.MemberPubKey()) require.NoError(t, err) cg1, err := peers[1].DB.OpenGroup(ctx, g, nil) diff --git a/testing.go b/testing.go index 97295262..157c655d 100644 --- a/testing.go +++ b/testing.go @@ -3,7 +3,6 @@ package weshnet import ( "bytes" "context" - crand "crypto/rand" "fmt" "io" "os" @@ -18,7 +17,6 @@ import ( grpc_ctxtags "github.com/grpc-ecosystem/go-grpc-middleware/tags" datastore "github.com/ipfs/go-datastore" ds_sync "github.com/ipfs/go-datastore/sync" - keystore "github.com/ipfs/go-ipfs-keystore" "github.com/libp2p/go-libp2p/core/crypto" "github.com/libp2p/go-libp2p/core/peer" libp2p_mocknet "github.com/libp2p/go-libp2p/p2p/net/mock" @@ -31,10 +29,10 @@ import ( orbitdb "berty.tech/go-orbit-db" "berty.tech/go-orbit-db/pubsub/pubsubraw" "berty.tech/weshnet/internal/datastoreutil" - "berty.tech/weshnet/pkg/cryptoutil" "berty.tech/weshnet/pkg/errcode" "berty.tech/weshnet/pkg/ipfsutil" "berty.tech/weshnet/pkg/protocoltypes" + "berty.tech/weshnet/pkg/secretstore" "berty.tech/weshnet/pkg/testutil" "berty.tech/weshnet/pkg/tinder" ) @@ -52,10 +50,17 @@ func NewTestOrbitDB(ctx context.Context, t *testing.T, logger *zap.Logger, node baseDS = datastoreutil.NewNamespacedDatastore(baseDS, datastore.NewKey(selfKey.ID().String())) + secretStore, err := secretstore.NewSecretStore(baseDS, nil) + require.NoError(t, err) + t.Cleanup(func() { + _ = secretStore.Close() + }) + pubSub := pubsubraw.NewPubSub(node.PubSub(), selfKey.ID(), logger, nil) odb, err := NewWeshOrbitDB(ctx, api, &NewOrbitDBOptions{ - Datastore: baseDS, + Datastore: baseDS, + SecretStore: secretStore, NewOrbitDBOptions: orbitdb.NewOrbitDBOptions{ Logger: logger, PubSub: pubSub, @@ -67,11 +72,10 @@ func NewTestOrbitDB(ctx context.Context, t *testing.T, logger *zap.Logger, node } type mockedPeer struct { - CoreAPI ipfsutil.CoreAPIMock - DB *WeshOrbitDB - GC *GroupContext - MKS *cryptoutil.MessageKeystore - DevKS cryptoutil.DeviceKeystore + CoreAPI ipfsutil.CoreAPIMock + DB *WeshOrbitDB + GC *GroupContext + SecretStore secretstore.SecretStore } func (m *mockedPeer) PeerInfo() peer.AddrInfo { @@ -84,18 +88,17 @@ type TestingProtocol struct { Service Service Client ServiceClient - RootDatastore datastore.Batching - DeviceKeystore cryptoutil.DeviceKeystore - IpfsCoreAPI ipfsutil.ExtendedCoreAPI - OrbitDB *WeshOrbitDB - GroupDatastore *cryptoutil.GroupDatastore + RootDatastore datastore.Batching + IpfsCoreAPI ipfsutil.ExtendedCoreAPI + OrbitDB *WeshOrbitDB + SecretStore secretstore.SecretStore } type TestingOpts struct { Logger *zap.Logger Mocknet libp2p_mocknet.Mocknet DiscoveryServer *tinder.MockDriverServer - DeviceKeystore cryptoutil.DeviceKeystore + SecretStore secretstore.SecretStore CoreAPIMock ipfsutil.CoreAPIMock OrbitDB *WeshOrbitDB ConnectFunc ConnectTestingProtocolFunc @@ -124,11 +127,13 @@ func NewTestingProtocol(ctx context.Context, t testing.TB, opts *TestingOpts, ds node = ipfsutil.TestingCoreAPIUsingMockNet(ctx, t, ipfsopts) } - deviceKeystore := opts.DeviceKeystore - if deviceKeystore == nil { - deviceKeystore = cryptoutil.NewDeviceKeystore( - ipfsutil.NewDatastoreKeystore(datastoreutil.NewNamespacedDatastore(ds, datastore.NewKey(NamespaceDeviceKeystore))), - nil) + secretStore := opts.SecretStore + if secretStore == nil { + var err error + secretStore, err = secretstore.NewInMemSecretStore(&secretstore.NewSecretStoreOptions{ + OutOfStorePrivateKey: opts.PushSK, + }) + require.NoError(t, err) } odb := opts.OrbitDB @@ -142,26 +147,22 @@ func NewTestingProtocol(ctx context.Context, t testing.TB, opts *TestingOpts, ds PubSub: pubSub, Logger: opts.Logger, }, - Datastore: ds, - DeviceKeystore: deviceKeystore, + Datastore: ds, + SecretStore: secretStore, }) require.NoError(t, err) } - groupDatastore, err := cryptoutil.NewGroupDatastore(ds) - require.NoError(t, err) - serviceOpts := Opts{ - Host: node.MockNode().PeerHost, - PubSub: node.PubSub(), - Logger: opts.Logger, - RootDatastore: ds, - DeviceKeystore: deviceKeystore, - IpfsCoreAPI: node.API(), - OrbitDB: odb, - TinderService: node.Tinder(), - PushKey: opts.PushSK, - GroupDatastore: groupDatastore, + Host: node.MockNode().PeerHost, + PubSub: node.PubSub(), + Logger: opts.Logger, + RootDatastore: ds, + IpfsCoreAPI: node.API(), + OrbitDB: odb, + TinderService: node.Tinder(), + OutOfStorePrivateKey: opts.PushSK, + SecretStore: secretStore, } service, cleanupService := TestingService(ctx, t, serviceOpts) @@ -194,11 +195,10 @@ func NewTestingProtocol(ctx context.Context, t testing.TB, opts *TestingOpts, ds Client: client, Service: service, - RootDatastore: ds, - DeviceKeystore: deviceKeystore, - IpfsCoreAPI: node.API(), - OrbitDB: odb, - GroupDatastore: serviceOpts.GroupDatastore, + RootDatastore: ds, + IpfsCoreAPI: node.API(), + OrbitDB: odb, + SecretStore: secretStore, } cleanup := func() { server.Stop() @@ -352,11 +352,11 @@ func CreatePeersWithGroupTest(ctx context.Context, t testing.TB, pathBase string logger, cleanupLogger := testutil.Logger(t) - var devKS cryptoutil.DeviceKeystore + var secretStore secretstore.SecretStore mockedPeers := make([]*mockedPeer, memberCount*deviceCount) - g, groupSK, err := NewGroupMultiMember() + group, groupPrivateKey, err := NewGroupMultiMember() if err != nil { t.Fatal(err) } @@ -377,42 +377,37 @@ func CreatePeersWithGroupTest(ctx context.Context, t testing.TB, pathBase string ca := ipfsutil.TestingCoreAPIUsingMockNet(ctx, t, &ipfsopts) if j == 0 { - devKS = cryptoutil.NewDeviceKeystore(keystore.NewMemKeystore(), nil) + secretStore, err = secretstore.NewInMemSecretStore(nil) + require.NoError(t, err) } else { - accSK, err := devKS.AccountPrivKey() - require.NoError(t, err, "deviceKeystore private key") - - accProofSK, err := devKS.AccountProofPrivKey() - require.NoError(t, err, "deviceKeystore private proof key") + privateKeyBytes, proofPrivateKeyBytes, err := secretStore.ExportAccountKeysForBackup() + require.NoError(t, err, "ExportAccountKeysForBackup error") - devKS, err = cryptoutil.NewWithExistingKeys(keystore.NewMemKeystore(), accSK, accProofSK) - require.NoError(t, err, "deviceKeystore from existing keys") + secretStore, err = secretstore.NewInMemSecretStore(nil) + require.NoError(t, err) + require.NoError(t, secretStore.ImportAccountKeys(privateKeyBytes, proofPrivateKeyBytes)) } - mk, cleanupMessageKeystore := cryptoutil.NewInMemMessageKeystore(logger) - db, err := NewWeshOrbitDB(ctx, ca.API(), &NewOrbitDBOptions{ NewOrbitDBOptions: orbitdb.NewOrbitDBOptions{ Logger: logger, }, - DeviceKeystore: devKS, - MessageKeystore: mk, + SecretStore: secretStore, }) if err != nil { t.Fatal(err) } - gc, err := db.OpenGroup(ctx, g, nil) + gc, err := db.OpenGroup(ctx, group, nil) if err != nil { t.Fatalf("err: creating new group context, %v", err) } mp := &mockedPeer{ - CoreAPI: ca, - DB: db, - GC: gc, - MKS: mk, - DevKS: devKS, + CoreAPI: ca, + DB: db, + GC: gc, + SecretStore: secretStore, } // setup cleanup @@ -431,7 +426,7 @@ func CreatePeersWithGroupTest(ctx context.Context, t testing.TB, pathBase string assert.NoError(t, err) } - cleanupMessageKeystore() + _ = secretStore.Close() } mockedPeers[deviceIndex] = mp @@ -441,7 +436,7 @@ func CreatePeersWithGroupTest(ctx context.Context, t testing.TB, pathBase string connectPeers(ctx, t, ipfsopts.Mocknet) - return mockedPeers, groupSK, func() { + return mockedPeers, groupPrivateKey, func() { for _, cleanup := range cls { cleanup() } @@ -489,34 +484,6 @@ type ServiceMethods interface { GetCurrentDevicePushConfig() (*protocoltypes.PushServiceReceiver, *protocoltypes.PushServer) } -func CreateVirtualOtherPeerSecretsShareSecret(t testing.TB, ctx context.Context, membersStores []*MetadataStore) (*cryptoutil.OwnMemberDevice, *protocoltypes.DeviceSecret) { - // Manually adding another member to the group - otherMemberSK, _, err := crypto.GenerateEd25519Key(crand.Reader) - require.NoError(t, err) - - otherDeviceSK, _, err := crypto.GenerateEd25519Key(crand.Reader) - require.NoError(t, err) - - otherMD := cryptoutil.NewOwnMemberDevice(otherMemberSK, otherDeviceSK) - ds, err := cryptoutil.NewDeviceSecret() - require.NoError(t, err) - - for _, store := range membersStores { - _, err = MetadataStoreAddDeviceToGroup(ctx, store, store.Group(), otherMD) - require.NoError(t, err) - - memPK, err := store.MemberPK() - require.NoError(t, err) - - _, err = MetadataStoreSendSecret(ctx, store, store.Group(), otherMD, memPK, ds) - require.NoError(t, err) - } - - time.Sleep(time.Millisecond * 200) - - return otherMD, ds -} - func GetRootDatastoreForPath(dir string, key []byte, salt []byte, logger *zap.Logger) (datastore.Batching, error) { inMemory := dir == InMemoryDir @@ -705,12 +672,12 @@ func CreateMultiMemberGroupInstance(ctx context.Context, t *testing.T, tps ...*T } func isEventAddSecretTargetedToMember(ownRawPK []byte, evt *protocoltypes.GroupMetadataEvent) ([]byte, error) { - // Only count EventTypeGroupDeviceSecretAdded events - if evt.Metadata.EventType != protocoltypes.EventTypeGroupDeviceSecretAdded { + // Only count EventTypeGroupDeviceChainKeyAdded events + if evt.Metadata.EventType != protocoltypes.EventTypeGroupDeviceChainKeyAdded { return nil, nil } - sec := &protocoltypes.GroupAddDeviceSecret{} + sec := &protocoltypes.GroupAddDeviceChainKey{} err := sec.Unmarshal(evt.Event) if err != nil { return nil, err