Skip to content

Commit

Permalink
fix: unifying multiple secret stores into a single interface
Browse files Browse the repository at this point in the history
Signed-off-by: Guillaume Louvigny <[email protected]>
  • Loading branch information
glouvigny committed Mar 30, 2023
1 parent 8d3f3fb commit 23ef09b
Show file tree
Hide file tree
Showing 66 changed files with 4,897 additions and 7,226 deletions.
62 changes: 21 additions & 41 deletions account_export.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand All @@ -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)
}

Expand Down Expand Up @@ -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 {
Expand All @@ -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)
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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"))
}
Expand All @@ -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) {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
}

Expand Down Expand Up @@ -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(
Expand Down
84 changes: 19 additions & 65 deletions account_export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand Down Expand Up @@ -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()
Expand All @@ -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) {
Expand Down Expand Up @@ -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)

Expand All @@ -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)
Expand Down
Loading

0 comments on commit 23ef09b

Please sign in to comment.