diff --git a/main.go b/main.go index be12a1ef5..51a00d0cd 100644 --- a/main.go +++ b/main.go @@ -18,8 +18,7 @@ func main() { //Host: "http://localhost:8080", }), DB: db, - CliKey: secrets.GetOrCreateClISecret(), - KeyHolder: secrets.NewKeyHolder(), + KeyHolder: secrets.NewKeyHolder(secrets.GetOrCreateClISecret()), } err = ctrl.Init() if err != nil { diff --git a/pkg/account.go b/pkg/account.go index 905134235..f11bbd6a2 100644 --- a/pkg/account.go +++ b/pkg/account.go @@ -72,7 +72,6 @@ func (c *ClICtrl) AddAccount(cxt context.Context) { func (c *ClICtrl) storeAccount(_ context.Context, email string, userID int64, app api.App, secretInfo *model.AccSecretInfo) error { // get password - secret := c.CliKey err := c.DB.Update(func(tx *bolt.Tx) error { b, err := tx.CreateBucketIfNotExists([]byte(AccBucket)) if err != nil { @@ -81,9 +80,9 @@ func (c *ClICtrl) storeAccount(_ context.Context, email string, userID int64, ap accInfo := model.Account{ Email: email, UserID: userID, - MasterKey: *model.MakeEncString(secretInfo.MasterKey, secret), - SecretKey: *model.MakeEncString(secretInfo.SecretKey, secret), - Token: *model.MakeEncString(secretInfo.Token, secret), + MasterKey: *model.MakeEncString(secretInfo.MasterKey, c.KeyHolder.DeviceKey), + SecretKey: *model.MakeEncString(secretInfo.SecretKey, c.KeyHolder.DeviceKey), + Token: *model.MakeEncString(secretInfo.Token, c.KeyHolder.DeviceKey), App: app, PublicKey: encoding.EncodeBase64(secretInfo.PublicKey), } diff --git a/pkg/cli.go b/pkg/cli.go index 3d2dd2ed0..ee80660c7 100644 --- a/pkg/cli.go +++ b/pkg/cli.go @@ -8,10 +8,8 @@ import ( ) type ClICtrl struct { - Client *api.Client - DB *bolt.DB - // CliKey is the key used to encrypt/decrypt sensitive data stored in the database - CliKey []byte + Client *api.Client + DB *bolt.DB KeyHolder *secrets.KeyHolder } diff --git a/pkg/mappers.go b/pkg/mappers.go index 510cad36d..9ac99f3b3 100644 --- a/pkg/mappers.go +++ b/pkg/mappers.go @@ -4,6 +4,7 @@ import ( "cli-go/internal/api" eCrypto "cli-go/internal/crypto" "cli-go/pkg/model" + "cli-go/pkg/secrets" "cli-go/utils/encoding" "context" "encoding/json" @@ -11,7 +12,7 @@ import ( "log" ) -func (c *ClICtrl) mapCollectionToAlbum(ctx context.Context, collection api.Collection) (*model.RemoteAlbum, error) { +func (c *ClICtrl) mapCollectionToAlbum(ctx context.Context, collection api.Collection, holder *secrets.KeyHolder) (*model.RemoteAlbum, error) { var album model.RemoteAlbum userID := ctx.Value("user_id").(int64) album.OwnerID = collection.Owner.ID @@ -19,11 +20,11 @@ func (c *ClICtrl) mapCollectionToAlbum(ctx context.Context, collection api.Colle album.IsShared = collection.Owner.ID != userID album.LastUpdatedAt = collection.UpdationTime album.IsDeleted = collection.IsDeleted - collectionKey, err := c.KeyHolder.GetCollectionKey(ctx, collection) + collectionKey, err := holder.GetCollectionKey(ctx, collection) if err != nil { return nil, err } - album.AlbumKey = *model.MakeEncString(collectionKey, c.CliKey) + album.AlbumKey = *model.MakeEncString(collectionKey, holder.DeviceKey) var name string if collection.EncryptedName != "" { decrName, err := eCrypto.SecretBoxOpenBase64(collection.EncryptedName, collection.NameDecryptionNonce, collectionKey) @@ -69,11 +70,11 @@ func (c *ClICtrl) mapCollectionToAlbum(ctx context.Context, collection api.Colle return &album, nil } -func (c *ClICtrl) mapApiFileToPhotoFile(ctx context.Context, album model.RemoteAlbum, file api.File) (*model.RemoteFile, error) { +func (c *ClICtrl) mapApiFileToPhotoFile(ctx context.Context, album model.RemoteAlbum, file api.File, holder *secrets.KeyHolder) (*model.RemoteFile, error) { if file.IsDeleted { return nil, errors.New("file is deleted") } - albumKey := album.AlbumKey.MustDecrypt(c.CliKey) + albumKey := album.AlbumKey.MustDecrypt(holder.DeviceKey) fileKey, err := eCrypto.SecretBoxOpen( encoding.DecodeBase64(file.EncryptedKey), encoding.DecodeBase64(file.KeyDecryptionNonce), @@ -84,7 +85,7 @@ func (c *ClICtrl) mapApiFileToPhotoFile(ctx context.Context, album model.RemoteA var photoFile model.RemoteFile photoFile.ID = file.ID photoFile.LastUpdateTime = file.UpdationTime - photoFile.Key = *model.MakeEncString(fileKey, c.CliKey) + photoFile.Key = *model.MakeEncString(fileKey, holder.DeviceKey) photoFile.FileNonce = file.File.DecryptionHeader photoFile.ThumbnailNonce = file.Thumbnail.DecryptionHeader photoFile.OwnerID = file.OwnerID diff --git a/pkg/remote_sync.go b/pkg/remote_sync.go index 6d6342bd7..fa0dc6828 100644 --- a/pkg/remote_sync.go +++ b/pkg/remote_sync.go @@ -25,7 +25,7 @@ func (c *ClICtrl) fetchRemoteCollections(ctx context.Context) error { if lastSyncTime == 0 && collection.IsDeleted { continue } - album, mapErr := c.mapCollectionToAlbum(ctx, collection) + album, mapErr := c.mapCollectionToAlbum(ctx, collection, c.KeyHolder) if mapErr != nil { return mapErr } @@ -84,7 +84,7 @@ func (c *ClICtrl) fetchRemoteFiles(ctx context.Context) error { // on first sync, no need to sync delete markers continue } - photoFile, err := c.mapApiFileToPhotoFile(ctx, album, file) + photoFile, err := c.mapApiFileToPhotoFile(ctx, album, file, c.KeyHolder) if err != nil { return err } diff --git a/pkg/secrets/key_holder.go b/pkg/secrets/key_holder.go index 336e3178f..621077b1f 100644 --- a/pkg/secrets/key_holder.go +++ b/pkg/secrets/key_holder.go @@ -10,14 +10,18 @@ import ( ) type KeyHolder struct { + // DeviceKey is the key used to encrypt/decrypt the data while storing sensitive + // information on the disk. Usually, it should be stored in OS Keychain. + DeviceKey []byte AccountSecrets map[string]*model.AccSecretInfo CollectionKeys map[string][]byte } -func NewKeyHolder() *KeyHolder { +func NewKeyHolder(deviceKey []byte) *KeyHolder { return &KeyHolder{ AccountSecrets: make(map[string]*model.AccSecretInfo), CollectionKeys: make(map[string][]byte), + DeviceKey: deviceKey, } } @@ -25,10 +29,10 @@ func NewKeyHolder() *KeyHolder { // It decrypts the token key, master key, and secret key using the CLI key. // The decrypted keys and the decoded public key are stored in the AccountSecrets map using the account key as the map key. // It returns the account secret information or an error if the decryption fails. -func (k *KeyHolder) LoadSecrets(account model.Account, cliKey []byte) (*model.AccSecretInfo, error) { - tokenKey := account.Token.MustDecrypt(cliKey) - masterKey := account.MasterKey.MustDecrypt(cliKey) - secretKey := account.SecretKey.MustDecrypt(cliKey) +func (k *KeyHolder) LoadSecrets(account model.Account) (*model.AccSecretInfo, error) { + tokenKey := account.Token.MustDecrypt(k.DeviceKey) + masterKey := account.MasterKey.MustDecrypt(k.DeviceKey) + secretKey := account.SecretKey.MustDecrypt(k.DeviceKey) k.AccountSecrets[account.AccountKey()] = &model.AccSecretInfo{ Token: tokenKey, MasterKey: masterKey, diff --git a/pkg/sync.go b/pkg/sync.go index e7742d29c..dd5680245 100644 --- a/pkg/sync.go +++ b/pkg/sync.go @@ -34,7 +34,7 @@ func (c *ClICtrl) StartSync() error { } func (c *ClICtrl) SyncAccount(account model.Account) error { - secretInfo, err := c.KeyHolder.LoadSecrets(account, c.CliKey) + secretInfo, err := c.KeyHolder.LoadSecrets(account) if err != nil { return err }