diff --git a/main.go b/main.go index be12a1ef598b8bf9d4dc01dbbd42ead63dce15fb..51a00d0cd2610a9083f3ba769c5cea9e4a94c867 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 905134235d37ddfd05d2967f85b015655fbbee72..f11bbd6a2a4cf3e52230673f02fafabd73036b04 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 3d2dd2ed08a2b02fe24ea14751977ccfb20435ec..ee80660c7449bba631cc607f01ba7221c88a11ab 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 510cad36d624b83b912e06a236f7428d5e5efe97..9ac99f3b3e021213d0ec280913a76b2937c5b53a 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 6d6342bd74c9b21de534ac53b4a66d420e5dc6b0..fa0dc6828dd89df04f377e049b1184b5c633c6ed 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 336e3178f6afd281f418007dd6bcee5cf52cbcde..621077b1f6b9864f1abb62302f8611845568f14a 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 e7742d29cb37258e2ae985007db480d5330483f3..dd568024593d973cca941065934798f10c81cca1 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 }