From 5a6c0b04ed13e3cec98e86d565bdfdd5ce5800ec Mon Sep 17 00:00:00 2001
From: Jake Taylor <jake@elixxir.io>
Date: Wed, 13 Jul 2022 12:10:24 -0500
Subject: [PATCH] added new version of CryptographicIdentity storage to support
 storing e2eDhKeys and loading legacy e2eDhKeys

---
 storage/user/cryptographic.go | 151 ++++++++++++++++++++++++++++------
 storage/user/info.go          |  35 +-------
 2 files changed, 130 insertions(+), 56 deletions(-)

diff --git a/storage/user/cryptographic.go b/storage/user/cryptographic.go
index fd4b73047..21e4265bf 100644
--- a/storage/user/cryptographic.go
+++ b/storage/user/cryptographic.go
@@ -10,15 +10,19 @@ package user
 import (
 	"bytes"
 	"encoding/gob"
+	"encoding/json"
 	"github.com/pkg/errors"
 	jww "github.com/spf13/jwalterweatherman"
+	"gitlab.com/elixxir/client/storage/utility"
 	"gitlab.com/elixxir/client/storage/versioned"
+	"gitlab.com/elixxir/crypto/cyclic"
 	"gitlab.com/xx_network/crypto/signature/rsa"
 	"gitlab.com/xx_network/primitives/id"
 	"gitlab.com/xx_network/primitives/netTime"
 )
 
-const currentCryptographicIdentityVersion = 0
+const originalCryptographicIdentityVersion = 0
+const currentCryptographicIdentityVersion = 1
 const cryptographicIdentityKey = "cryptographicIdentity"
 
 type CryptographicIdentity struct {
@@ -29,6 +33,8 @@ type CryptographicIdentity struct {
 	receptionSalt      []byte
 	receptionRsaKey    *rsa.PrivateKey
 	isPrecanned        bool
+	e2eDhPrivateKey    *cyclic.Int
+	e2eDhPublicKey     *cyclic.Int
 }
 
 type ciDisk struct {
@@ -41,10 +47,23 @@ type ciDisk struct {
 	IsPrecanned        bool
 }
 
+type ciDiskV1 struct {
+	TransmissionID     *id.ID
+	TransmissionSalt   []byte
+	TransmissionRsaKey *rsa.PrivateKey
+	ReceptionID        *id.ID
+	ReceptionSalt      []byte
+	ReceptionRsaKey    *rsa.PrivateKey
+	IsPrecanned        bool
+	E2eDhPrivateKey    []byte
+	E2eDhPublicKey     []byte
+}
+
 func newCryptographicIdentity(transmissionID, receptionID *id.ID,
 	transmissionSalt, receptionSalt []byte,
 	transmissionRsa, receptionRsa *rsa.PrivateKey,
-	isPrecanned bool, kv *versioned.KV) *CryptographicIdentity {
+	isPrecanned bool, e2eDhPrivateKey, e2eDhPublicKey *cyclic.Int,
+	kv *versioned.KV) *CryptographicIdentity {
 
 	ci := &CryptographicIdentity{
 		transmissionID:     transmissionID,
@@ -54,6 +73,8 @@ func newCryptographicIdentity(transmissionID, receptionID *id.ID,
 		receptionSalt:      receptionSalt,
 		receptionRsaKey:    receptionRsa,
 		isPrecanned:        isPrecanned,
+		e2eDhPrivateKey:    e2eDhPrivateKey,
+		e2eDhPublicKey:     e2eDhPublicKey,
 	}
 
 	if err := ci.save(kv); err != nil {
@@ -64,39 +85,114 @@ func newCryptographicIdentity(transmissionID, receptionID *id.ID,
 	return ci
 }
 
-func loadCryptographicIdentity(kv *versioned.KV) (*CryptographicIdentity, error) {
-	obj, err := kv.Get(cryptographicIdentityKey,
-		currentCryptographicIdentityVersion)
+// loadOriginalCryptographicIdentity attempts to load the originalCryptographicIdentityVersion CryptographicIdentity
+func loadOriginalCryptographicIdentity(kv *versioned.KV) (*CryptographicIdentity, error) {
+	result := &CryptographicIdentity{}
+	obj, err := kv.Get(cryptographicIdentityKey, originalCryptographicIdentityVersion)
 	if err != nil {
-		return nil, errors.WithMessage(err, "Failed to get user "+
-			"cryptographic identity from EKV")
+		return nil, errors.WithMessagef(err, "Failed to get version %d user "+
+			"cryptographic identity from EKV", originalCryptographicIdentityVersion)
 	}
-
 	var resultBuffer bytes.Buffer
-	result := &CryptographicIdentity{}
 	decodable := &ciDisk{}
 
 	resultBuffer.Write(obj.Data)
 	dec := gob.NewDecoder(&resultBuffer)
 	err = dec.Decode(decodable)
+	if err != nil {
+		return nil, err
+	}
 
-	if decodable != nil {
-		result.isPrecanned = decodable.IsPrecanned
-		result.receptionRsaKey = decodable.ReceptionRsaKey
-		result.transmissionRsaKey = decodable.TransmissionRsaKey
-		result.transmissionSalt = decodable.TransmissionSalt
-		result.transmissionID = decodable.TransmissionID
-		result.receptionID = decodable.ReceptionID
-		result.receptionSalt = decodable.ReceptionSalt
+	result.isPrecanned = decodable.IsPrecanned
+	result.receptionRsaKey = decodable.ReceptionRsaKey
+	result.transmissionRsaKey = decodable.TransmissionRsaKey
+	result.transmissionSalt = decodable.TransmissionSalt
+	result.transmissionID = decodable.TransmissionID
+	result.receptionID = decodable.ReceptionID
+	result.receptionSalt = decodable.ReceptionSalt
+	return result, nil
+}
+
+func loadCryptographicIdentity(kv *versioned.KV) (*CryptographicIdentity, error) {
+	result := &CryptographicIdentity{}
+	obj, err := kv.Get(cryptographicIdentityKey,
+		currentCryptographicIdentityVersion)
+	if err != nil {
+		result, err = loadOriginalCryptographicIdentity(kv)
+		if err != nil {
+			return nil, err
+		}
+		// Populate E2E keys from legacy storage
+		result.e2eDhPublicKey, result.e2eDhPrivateKey = loadLegacyDHKeys(kv)
+		// Migrate to the new version in storage
+		return result, result.save(kv)
+	}
+
+	decodable := &ciDiskV1{}
+	err = json.Unmarshal(obj.Data, decodable)
+	if err != nil {
+		return nil, err
 	}
 
-	return result, err
+	result.isPrecanned = decodable.IsPrecanned
+	result.receptionRsaKey = decodable.ReceptionRsaKey
+	result.transmissionRsaKey = decodable.TransmissionRsaKey
+	result.transmissionSalt = decodable.TransmissionSalt
+	result.transmissionID = decodable.TransmissionID
+	result.receptionID = decodable.ReceptionID
+	result.receptionSalt = decodable.ReceptionSalt
+
+	result.e2eDhPrivateKey = &cyclic.Int{}
+	err = result.e2eDhPrivateKey.UnmarshalJSON(decodable.E2eDhPrivateKey)
+	if err != nil {
+		return nil, err
+	}
+	result.e2eDhPublicKey = &cyclic.Int{}
+	err = result.e2eDhPublicKey.UnmarshalJSON(decodable.E2eDhPublicKey)
+	if err != nil {
+		return nil, err
+	}
+
+	return result, nil
+}
+
+// loadLegacyDHKeys attempts to load DH Keys from legacy storage. It
+// prints a warning to the log as users should be using ReceptionIdentity
+// instead of PortableUserInfo
+func loadLegacyDHKeys(kv *versioned.KV) (pub, priv *cyclic.Int) {
+	// Legacy package prefixes and keys, see e2e/ratchet/storage.go
+	packagePrefix := "e2eSession"
+	pubKeyKey := "DhPubKey"
+	privKeyKey := "DhPrivKey"
+
+	kvPrefix := kv.Prefix(packagePrefix)
+
+	privKey, err := utility.LoadCyclicKey(kvPrefix, privKeyKey)
+	if err != nil {
+		jww.ERROR.Printf("Failed to load e2e DH private key: %v", err)
+		return nil, nil
+	}
+
+	pubKey, err := utility.LoadCyclicKey(kvPrefix, pubKeyKey)
+	if err != nil {
+		jww.ERROR.Printf("Failed to load e2e DH public key: %v", err)
+		return nil, nil
+	}
+
+	return pubKey, privKey
 }
 
 func (ci *CryptographicIdentity) save(kv *versioned.KV) error {
-	var userDataBuffer bytes.Buffer
+	dhPriv, err := ci.e2eDhPrivateKey.MarshalJSON()
+	if err != nil {
+		return err
+	}
+	dhPub, err := ci.e2eDhPublicKey.MarshalJSON()
+	if err != nil {
+		return err
+	}
 
-	encodable := &ciDisk{
+	encodable := &ciDiskV1{
 		TransmissionID:     ci.transmissionID,
 		TransmissionSalt:   ci.transmissionSalt,
 		TransmissionRsaKey: ci.transmissionRsaKey,
@@ -104,10 +200,11 @@ func (ci *CryptographicIdentity) save(kv *versioned.KV) error {
 		ReceptionSalt:      ci.receptionSalt,
 		ReceptionRsaKey:    ci.receptionRsaKey,
 		IsPrecanned:        ci.isPrecanned,
+		E2eDhPrivateKey:    dhPriv,
+		E2eDhPublicKey:     dhPub,
 	}
 
-	enc := gob.NewEncoder(&userDataBuffer)
-	err := enc.Encode(encodable)
+	enc, err := json.Marshal(&encodable)
 	if err != nil {
 		return err
 	}
@@ -115,7 +212,7 @@ func (ci *CryptographicIdentity) save(kv *versioned.KV) error {
 	obj := &versioned.Object{
 		Version:   currentCryptographicIdentityVersion,
 		Timestamp: netTime.Now(),
-		Data:      userDataBuffer.Bytes(),
+		Data:      enc,
 	}
 
 	return kv.Set(cryptographicIdentityKey,
@@ -149,3 +246,11 @@ func (ci *CryptographicIdentity) GetTransmissionRSA() *rsa.PrivateKey {
 func (ci *CryptographicIdentity) IsPrecanned() bool {
 	return ci.isPrecanned
 }
+
+func (ci *CryptographicIdentity) GetE2eDhPublicKey() *cyclic.Int {
+	return ci.e2eDhPublicKey.DeepCopy()
+}
+
+func (ci *CryptographicIdentity) GetE2eDhPrivateKey() *cyclic.Int {
+	return ci.e2eDhPrivateKey.DeepCopy()
+}
diff --git a/storage/user/info.go b/storage/user/info.go
index ab8fcd8ba..fc272cb0f 100644
--- a/storage/user/info.go
+++ b/storage/user/info.go
@@ -8,8 +8,6 @@
 package user
 
 import (
-	jww "github.com/spf13/jwalterweatherman"
-	"gitlab.com/elixxir/client/storage/utility"
 	"gitlab.com/elixxir/crypto/backup"
 	"gitlab.com/elixxir/crypto/cyclic"
 	"gitlab.com/xx_network/crypto/signature/rsa"
@@ -87,7 +85,6 @@ func NewUserFromBackup(backup *backup.Backup) Info {
 
 func (u *User) PortableUserInfo() Info {
 	ci := u.CryptographicIdentity
-	pub, priv := u.loadLegacyDHKeys()
 	return Info{
 		TransmissionID:        ci.GetTransmissionID().DeepCopy(),
 		TransmissionSalt:      copySlice(ci.GetTransmissionSalt()),
@@ -97,40 +94,12 @@ func (u *User) PortableUserInfo() Info {
 		ReceptionSalt:         copySlice(ci.GetReceptionSalt()),
 		ReceptionRSA:          ci.GetReceptionRSA(),
 		Precanned:             ci.IsPrecanned(),
-		E2eDhPrivateKey:       priv,
-		E2eDhPublicKey:        pub,
+		E2eDhPrivateKey:       ci.GetE2eDhPrivateKey(),
+		E2eDhPublicKey:        ci.GetE2eDhPublicKey(),
 	}
 
 }
 
-// loadLegacyDHKeys attempts to load DH Keys from legacy storage. It
-// prints a warning to the log as users should be using ReceptionIdentity
-// instead of PortableUserInfo
-func (u *User) loadLegacyDHKeys() (pub, priv *cyclic.Int) {
-	jww.WARN.Printf("loading legacy e2e keys in cmix layer, please " +
-		"update your code to use xxdk.ReceptionIdentity")
-	// Legacy package prefixes and keys, see e2e/ratchet/storage.go
-	packagePrefix := "e2eSession"
-	pubKeyKey := "DhPubKey"
-	privKeyKey := "DhPrivKey"
-
-	kv := u.kv.Prefix(packagePrefix)
-
-	privKey, err := utility.LoadCyclicKey(kv, privKeyKey)
-	if err != nil {
-		jww.ERROR.Printf("Failed to load e2e DH private key: %v", err)
-		return nil, nil
-	}
-
-	pubKey, err := utility.LoadCyclicKey(kv, pubKeyKey)
-	if err != nil {
-		jww.ERROR.Printf("Failed to load e2e DH public key: %v", err)
-		return nil, nil
-	}
-
-	return pubKey, privKey
-}
-
 func copySlice(s []byte) []byte {
 	n := make([]byte, len(s))
 	copy(n, s)
-- 
GitLab