Skip to content

Commit

Permalink
feat: removed group pk from push relay accessible information
Browse files Browse the repository at this point in the history
Signed-off-by: Guillaume Louvigny <glouvigny@users.noreply.github.com>
  • Loading branch information
glouvigny committed Nov 18, 2021
1 parent 059c931 commit 2049b99
Show file tree
Hide file tree
Showing 10 changed files with 879 additions and 479 deletions.
5 changes: 5 additions & 0 deletions api/protocoltypes.proto
Original file line number Diff line number Diff line change
Expand Up @@ -1487,3 +1487,8 @@ message PushSetServer {
message Reply {}
}


message FirstLastCounters {
uint64 first = 1;
uint64 last = 2;
}
2 changes: 1 addition & 1 deletion api/pushtypes.proto
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ message PushServiceSend {
message OutOfStoreMessageEnvelope {
bytes nonce = 1;
bytes box = 2;
bytes group_public_key = 3;
bytes group_reference = 4 [(gogoproto.customname) = "GroupReference"];
}

message PushExposedData {
Expand Down
10 changes: 10 additions & 0 deletions docs/apis/protocoltypes.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions docs/gen.sum

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

39 changes: 38 additions & 1 deletion go/internal/cryptoutil/group.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package cryptoutil

import (
"encoding/binary"
"fmt"
"io"

"golang.org/x/crypto/hkdf"
Expand All @@ -9,9 +11,15 @@ import (
"berty.tech/berty/v2/go/pkg/errcode"
)

type GroupWithLinkKey interface {
const PushSecretNamespace = "push_secret_ref" // nolint:gosec

type GroupWithSecret interface {
GetPublicKey() []byte
GetSecret() []byte
}

type GroupWithLinkKey interface {
GroupWithSecret
GetLinkKey() []byte
}

Expand Down Expand Up @@ -46,3 +54,32 @@ func GetSharedSecret(m GroupWithLinkKey) *[KeySize]byte {

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
}
112 changes: 112 additions & 0 deletions go/internal/cryptoutil/keystore_message.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package cryptoutil

import (
"context"
"encoding/base64"
"fmt"
"sync"

Expand All @@ -15,6 +16,8 @@ import (
"berty.tech/berty/v2/go/pkg/protocoltypes"
)

const precomputePushRefsCount = 100

type MessageKeystore struct {
lock sync.Mutex
preComputedKeysCount int
Expand Down Expand Up @@ -211,6 +214,14 @@ func (m *MessageKeystore) registerChainKey(g *protocoltypes.Group, devicePK cryp
return errcode.ErrInternal.Wrap(err)
}

devicePKBytes, err := devicePK.Raw()
if err == nil {
if err := m.UpdatePushGroupReferences(devicePKBytes, ds.Counter, g); err != nil {
// TODO: log
_ = err
}
}

return nil
}

Expand Down Expand Up @@ -685,3 +696,104 @@ func (m *MessageKeystore) OpenOutOfStoreMessage(envelope *protocoltypes.OutOfSto

return clear, di.NewlyDecrypted, nil
}

func (m *MessageKeystore) refKey(ref []byte) datastore.Key {
return datastore.KeyWithNamespaces([]string{
"push-refs", base64.RawURLEncoding.EncodeToString(ref),
})
}

func (m *MessageKeystore) refFirstLastKey(groupPK, devicePK []byte) datastore.Key {
return datastore.KeyWithNamespaces([]string{
"push-refs",
base64.RawURLEncoding.EncodeToString(groupPK),
base64.RawURLEncoding.EncodeToString(devicePK),
})
}

func (m *MessageKeystore) GetByPushGroupReference(ref []byte) ([]byte, error) {
return m.store.Get(m.refKey(ref))
}

func (m *MessageKeystore) UpdatePushGroupReferences(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(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 {
// TODO: log
continue
}

if err := m.store.Delete(m.refKey(ref)); err != nil {
// TODO: log
continue
}
}

// Add new refs
for i := 0; i < len(refsToCreate); i++ {
ref, err := CreatePushGroupReference(devicePK, refsToCreate[i], groupPushSecret)
if err != nil {
// TODO: log
continue
}

if err := m.store.Put(m.refKey(ref), group.GetPublicKey()); err != nil {
// TODO: log
continue
}
}

return nil
}

func (m *MessageKeystore) firstLastCachedGroupRefsForMember(devicePK []byte, group GroupWithSecret) (uint64, uint64, error) {
key := m.refFirstLastKey(group.GetPublicKey(), devicePK)
bytes, err := m.store.Get(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
}
16 changes: 15 additions & 1 deletion go/pkg/bertyprotocol/store_message.go
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,10 @@ func constructorFactoryGroupMessage(s *BertyOrbitDB, logger *zap.Logger) iface.S
continue
}

if err := s.messageKeystore.UpdatePushGroupReferences(messageEvent.Headers.DevicePK, messageEvent.Headers.Counter, g); err != nil {
store.logger.Error("unable to update push group references", zap.Error(err))
}

store.logger.Debug(
"Got message store payload",
tyber.FormatStepLogFields(ctx, []tyber.Detail{{Name: "Payload", Description: string(messageEvent.Message)}}, tyber.EndTrace)...,
Expand Down Expand Up @@ -389,9 +393,19 @@ func SealOutOfStoreMessageEnvelope(id cid.Cid, env *protocoltypes.MessageEnvelop

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)
if err != nil {
return nil, errcode.ErrCryptoKeyGeneration.Wrap(err)
}

return &pushtypes.OutOfStoreMessageEnvelope{
Nonce: nonce[:],
Box: encryptedData,
GroupPublicKey: g.PublicKey,
GroupReference: pushGroupRef,
}, nil
}
18 changes: 15 additions & 3 deletions go/pkg/bertypush/push_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,12 @@ func (s *pushHandler) PushReceive(payload []byte) (*protocoltypes.PushReceive_Re
return nil, errcode.ErrDeserialization.Wrap(err)
}

gPK, err := crypto.UnmarshalEd25519PublicKey(oosMessageEnv.GroupPublicKey)
gPKBytes, err := s.messageKeystore.GetByPushGroupReference(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)
}
Expand All @@ -152,15 +157,22 @@ func (s *pushHandler) PushReceive(payload []byte) (*protocoltypes.PushReceive_Re
return nil, errcode.ErrCryptoDecrypt.Wrap(err)
}

clear, newlyDecrypted, err := s.messageKeystore.OpenOutOfStoreMessage(oosMessage, oosMessageEnv.GroupPublicKey)
clear, newlyDecrypted, err := s.messageKeystore.OpenOutOfStoreMessage(oosMessage, gPKBytes)
if err != nil {
return nil, errcode.ErrCryptoDecrypt.Wrap(err)
}

g, err := s.groupDatastore.Get(gPK)
if err == nil {
if err := s.messageKeystore.UpdatePushGroupReferences(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: oosMessageEnv.GroupPublicKey,
GroupPublicKey: gPKBytes,
AlreadyReceived: !newlyDecrypted,
}, nil
}
Expand Down
Loading

0 comments on commit 2049b99

Please sign in to comment.