From 73af752f5285ff29bc59dc97ce6c67bc3fede1bc Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Sat, 18 Jan 2025 16:08:56 +0530 Subject: [PATCH 1/7] [mob] LowerMem & increase ops limit for key derivation --- mobile/lib/utils/crypto_util.dart | 40 ++++++++++++++++++------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/mobile/lib/utils/crypto_util.dart b/mobile/lib/utils/crypto_util.dart index 2005e7882f..e48ca1587d 100644 --- a/mobile/lib/utils/crypto_util.dart +++ b/mobile/lib/utils/crypto_util.dart @@ -18,6 +18,8 @@ const int loginSubKeyLen = 32; const int loginSubKeyId = 1; const String loginSubKeyContext = "loginctx"; +const int _keyDerivcationStregth = 1073741824 * 4; + Uint8List cryptoSecretboxEasy(Map args) { return Sodium.cryptoSecretboxEasy(args["source"], args["nonce"], args["key"]); } @@ -392,24 +394,28 @@ class CryptoUtil { Uint8List salt, ) async { final logger = Logger("pwhash"); - int memLimit = Sodium.cryptoPwhashMemlimitSensitive; - int opsLimit = Sodium.cryptoPwhashOpslimitSensitive; - if (await isLowSpecDevice()) { - logger.info("low spec device detected"); - // When sensitive memLimit (1 GB) is used, on low spec device the OS might - // kill the app with OOM. To avoid that, start with 256 MB and - // corresponding ops limit (16). - // This ensures that the product of these two variables - // (the area under the graph that determines the amount of work required) - // stays the same - // SODIUM_CRYPTO_PWHASH_MEMLIMIT_SENSITIVE: 1073741824 - // SODIUM_CRYPTO_PWHASH_MEMLIMIT_MODERATE: 268435456 - // SODIUM_CRYPTO_PWHASH_OPSLIMIT_SENSITIVE: 4 - memLimit = Sodium.cryptoPwhashMemlimitModerate; - final factor = Sodium.cryptoPwhashMemlimitSensitive ~/ - Sodium.cryptoPwhashMemlimitModerate; // = 4 - opsLimit = opsLimit * factor; // = 16 + final int desiredStrength = Sodium.cryptoPwhashMemlimitSensitive * + Sodium.cryptoPwhashOpslimitSensitive; + // When sensitive memLimit (1 GB) is used, on low spec device the OS might + // kill the app with OOM. To avoid that, start with 256 MB and + // corresponding ops limit (16). + // This ensures that the product of these two variables + // (the area under the graph that determines the amount of work required) + // stays the same + // SODIUM_CRYPTO_PWHASH_MEMLIMIT_SENSITIVE: 1073741824 + // SODIUM_CRYPTO_PWHASH_MEMLIMIT_MODERATE: 268435456 + // SODIUM_CRYPTO_PWHASH_OPSLIMIT_SENSITIVE: 4 + int memLimit = Sodium.cryptoPwhashMemlimitModerate; + final factor = Sodium.cryptoPwhashMemlimitSensitive ~/ + Sodium.cryptoPwhashMemlimitModerate; // = 4 + int opsLimit = Sodium.cryptoPwhashOpslimitSensitive * factor; // = 16 + if (memLimit * opsLimit != desiredStrength || + desiredStrength != _keyDerivcationStregth) { + throw UnsupportedError( + "unexpcted values for memLimit $memLimit and opsLimit: $opsLimit or desiredStrength: $desiredStrength", + ); } + Uint8List key; while (memLimit >= Sodium.cryptoPwhashMemlimitMin && opsLimit <= Sodium.cryptoPwhashOpslimitMax) { From 43adef8899c6605cb719b10e02a5bafbf9f10fde Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Sat, 18 Jan 2025 16:14:00 +0530 Subject: [PATCH 2/7] [mob] Fixed typo and lint --- mobile/lib/utils/crypto_util.dart | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/mobile/lib/utils/crypto_util.dart b/mobile/lib/utils/crypto_util.dart index e48ca1587d..d9d515d47e 100644 --- a/mobile/lib/utils/crypto_util.dart +++ b/mobile/lib/utils/crypto_util.dart @@ -8,7 +8,6 @@ import 'package:logging/logging.dart'; import "package:photos/core/errors.dart"; import 'package:photos/models/derived_key_result.dart'; import 'package:photos/models/encryption_result.dart'; -import "package:photos/utils/device_info.dart"; const int encryptionChunkSize = 4 * 1024 * 1024; final int decryptionChunkSize = @@ -18,7 +17,7 @@ const int loginSubKeyLen = 32; const int loginSubKeyId = 1; const String loginSubKeyContext = "loginctx"; -const int _keyDerivcationStregth = 1073741824 * 4; +const int _keyDerivationStregth = 1073741824 * 4; Uint8List cryptoSecretboxEasy(Map args) { return Sodium.cryptoSecretboxEasy(args["source"], args["nonce"], args["key"]); @@ -410,7 +409,7 @@ class CryptoUtil { Sodium.cryptoPwhashMemlimitModerate; // = 4 int opsLimit = Sodium.cryptoPwhashOpslimitSensitive * factor; // = 16 if (memLimit * opsLimit != desiredStrength || - desiredStrength != _keyDerivcationStregth) { + desiredStrength != _keyDerivationStregth) { throw UnsupportedError( "unexpcted values for memLimit $memLimit and opsLimit: $opsLimit or desiredStrength: $desiredStrength", ); From 8e26433dd26aa01ae7be9070d9c1a3bb5bd67f05 Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Sat, 18 Jan 2025 16:08:56 +0530 Subject: [PATCH 3/7] [mob] LowerMem & increase ops limit for key derivation --- mobile/lib/utils/crypto_util.dart | 40 ++++++++++++++++++------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/mobile/lib/utils/crypto_util.dart b/mobile/lib/utils/crypto_util.dart index 2005e7882f..e48ca1587d 100644 --- a/mobile/lib/utils/crypto_util.dart +++ b/mobile/lib/utils/crypto_util.dart @@ -18,6 +18,8 @@ const int loginSubKeyLen = 32; const int loginSubKeyId = 1; const String loginSubKeyContext = "loginctx"; +const int _keyDerivcationStregth = 1073741824 * 4; + Uint8List cryptoSecretboxEasy(Map args) { return Sodium.cryptoSecretboxEasy(args["source"], args["nonce"], args["key"]); } @@ -392,24 +394,28 @@ class CryptoUtil { Uint8List salt, ) async { final logger = Logger("pwhash"); - int memLimit = Sodium.cryptoPwhashMemlimitSensitive; - int opsLimit = Sodium.cryptoPwhashOpslimitSensitive; - if (await isLowSpecDevice()) { - logger.info("low spec device detected"); - // When sensitive memLimit (1 GB) is used, on low spec device the OS might - // kill the app with OOM. To avoid that, start with 256 MB and - // corresponding ops limit (16). - // This ensures that the product of these two variables - // (the area under the graph that determines the amount of work required) - // stays the same - // SODIUM_CRYPTO_PWHASH_MEMLIMIT_SENSITIVE: 1073741824 - // SODIUM_CRYPTO_PWHASH_MEMLIMIT_MODERATE: 268435456 - // SODIUM_CRYPTO_PWHASH_OPSLIMIT_SENSITIVE: 4 - memLimit = Sodium.cryptoPwhashMemlimitModerate; - final factor = Sodium.cryptoPwhashMemlimitSensitive ~/ - Sodium.cryptoPwhashMemlimitModerate; // = 4 - opsLimit = opsLimit * factor; // = 16 + final int desiredStrength = Sodium.cryptoPwhashMemlimitSensitive * + Sodium.cryptoPwhashOpslimitSensitive; + // When sensitive memLimit (1 GB) is used, on low spec device the OS might + // kill the app with OOM. To avoid that, start with 256 MB and + // corresponding ops limit (16). + // This ensures that the product of these two variables + // (the area under the graph that determines the amount of work required) + // stays the same + // SODIUM_CRYPTO_PWHASH_MEMLIMIT_SENSITIVE: 1073741824 + // SODIUM_CRYPTO_PWHASH_MEMLIMIT_MODERATE: 268435456 + // SODIUM_CRYPTO_PWHASH_OPSLIMIT_SENSITIVE: 4 + int memLimit = Sodium.cryptoPwhashMemlimitModerate; + final factor = Sodium.cryptoPwhashMemlimitSensitive ~/ + Sodium.cryptoPwhashMemlimitModerate; // = 4 + int opsLimit = Sodium.cryptoPwhashOpslimitSensitive * factor; // = 16 + if (memLimit * opsLimit != desiredStrength || + desiredStrength != _keyDerivcationStregth) { + throw UnsupportedError( + "unexpcted values for memLimit $memLimit and opsLimit: $opsLimit or desiredStrength: $desiredStrength", + ); } + Uint8List key; while (memLimit >= Sodium.cryptoPwhashMemlimitMin && opsLimit <= Sodium.cryptoPwhashOpslimitMax) { From ffde143247e50d6e55848ac3bf5a4dd0decf8352 Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Sat, 18 Jan 2025 16:14:00 +0530 Subject: [PATCH 4/7] [mob] Fixed typo and lint --- mobile/lib/utils/crypto_util.dart | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/mobile/lib/utils/crypto_util.dart b/mobile/lib/utils/crypto_util.dart index e48ca1587d..d9d515d47e 100644 --- a/mobile/lib/utils/crypto_util.dart +++ b/mobile/lib/utils/crypto_util.dart @@ -8,7 +8,6 @@ import 'package:logging/logging.dart'; import "package:photos/core/errors.dart"; import 'package:photos/models/derived_key_result.dart'; import 'package:photos/models/encryption_result.dart'; -import "package:photos/utils/device_info.dart"; const int encryptionChunkSize = 4 * 1024 * 1024; final int decryptionChunkSize = @@ -18,7 +17,7 @@ const int loginSubKeyLen = 32; const int loginSubKeyId = 1; const String loginSubKeyContext = "loginctx"; -const int _keyDerivcationStregth = 1073741824 * 4; +const int _keyDerivationStregth = 1073741824 * 4; Uint8List cryptoSecretboxEasy(Map args) { return Sodium.cryptoSecretboxEasy(args["source"], args["nonce"], args["key"]); @@ -410,7 +409,7 @@ class CryptoUtil { Sodium.cryptoPwhashMemlimitModerate; // = 4 int opsLimit = Sodium.cryptoPwhashOpslimitSensitive * factor; // = 16 if (memLimit * opsLimit != desiredStrength || - desiredStrength != _keyDerivcationStregth) { + desiredStrength != _keyDerivationStregth) { throw UnsupportedError( "unexpcted values for memLimit $memLimit and opsLimit: $opsLimit or desiredStrength: $desiredStrength", ); From 02fb6888abaaaf2cf559055e458032ec41796524 Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Mon, 20 Jan 2025 14:12:59 +0530 Subject: [PATCH 5/7] [mob] Remove client side strength check --- mobile/lib/utils/crypto_util.dart | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/mobile/lib/utils/crypto_util.dart b/mobile/lib/utils/crypto_util.dart index d9d515d47e..52ed85f857 100644 --- a/mobile/lib/utils/crypto_util.dart +++ b/mobile/lib/utils/crypto_util.dart @@ -17,8 +17,6 @@ const int loginSubKeyLen = 32; const int loginSubKeyId = 1; const String loginSubKeyContext = "loginctx"; -const int _keyDerivationStregth = 1073741824 * 4; - Uint8List cryptoSecretboxEasy(Map args) { return Sodium.cryptoSecretboxEasy(args["source"], args["nonce"], args["key"]); } @@ -408,10 +406,9 @@ class CryptoUtil { final factor = Sodium.cryptoPwhashMemlimitSensitive ~/ Sodium.cryptoPwhashMemlimitModerate; // = 4 int opsLimit = Sodium.cryptoPwhashOpslimitSensitive * factor; // = 16 - if (memLimit * opsLimit != desiredStrength || - desiredStrength != _keyDerivationStregth) { + if (memLimit * opsLimit != desiredStrength) { throw UnsupportedError( - "unexpcted values for memLimit $memLimit and opsLimit: $opsLimit or desiredStrength: $desiredStrength", + "unexpcted values for memLimit $memLimit and opsLimit: $opsLimit", ); } From 2b8c6494cd0b77b323686ec0de53488f711ee24c Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Mon, 20 Jan 2025 14:16:39 +0530 Subject: [PATCH 6/7] [mob] Remove unused value --- mobile/lib/utils/crypto_util.dart | 2 -- 1 file changed, 2 deletions(-) diff --git a/mobile/lib/utils/crypto_util.dart b/mobile/lib/utils/crypto_util.dart index ef155de54d..52ed85f857 100644 --- a/mobile/lib/utils/crypto_util.dart +++ b/mobile/lib/utils/crypto_util.dart @@ -17,8 +17,6 @@ const int loginSubKeyLen = 32; const int loginSubKeyId = 1; const String loginSubKeyContext = "loginctx"; -const int _keyDerivationStregth = 1073741824 * 4; - Uint8List cryptoSecretboxEasy(Map args) { return Sodium.cryptoSecretboxEasy(args["source"], args["nonce"], args["key"]); } From ff3da665a3811b4039ce4fdc289df159714cd2bf Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Mon, 20 Jan 2025 14:12:02 +0530 Subject: [PATCH 7/7] [server] Add server side validation --- server/ente/user.go | 18 ++++++++++++++++++ server/pkg/api/user.go | 4 ++++ server/pkg/repo/srp.go | 3 +++ 3 files changed, 25 insertions(+) diff --git a/server/ente/user.go b/server/ente/user.go index 9ab12f31a4..8b207712d9 100644 --- a/server/ente/user.go +++ b/server/ente/user.go @@ -10,6 +10,8 @@ const ( ChangeEmailOTTPurpose = "change" SignUpOTTPurpose = "signup" LoginOTTPurpose = "login" + + ExpectedKDFStrength = 1073741824 * 4 ) // User represents a user in the system @@ -88,6 +90,14 @@ type SetUserAttributesRequest struct { KeyAttributes KeyAttributes `json:"keyAttributes" binding:"required"` } +func (sk *SetUserAttributesRequest) Validate() error { + strength := sk.KeyAttributes.MemLimit * sk.KeyAttributes.OpsLimit + if strength != ExpectedKDFStrength { + return NewBadRequestWithMessage("Unexpected KDF strength") + } + return nil +} + // UpdateEmailMFA .. type UpdateEmailMFA struct { IsEnabled *bool `json:"isEnabled" binding:"required"` @@ -102,6 +112,14 @@ type UpdateKeysRequest struct { OpsLimit int `json:"opsLimit" binding:"required"` } +func (u *UpdateKeysRequest) Validate() error { + strength := u.MemLimit * u.OpsLimit + if strength != ExpectedKDFStrength { + return NewBadRequestWithMessage("Unexpected KDF strength") + } + return nil +} + type SetRecoveryKeyRequest struct { MasterKeyEncryptedWithRecoveryKey string `json:"masterKeyEncryptedWithRecoveryKey"` MasterKeyDecryptionNonce string `json:"masterKeyDecryptionNonce"` diff --git a/server/pkg/api/user.go b/server/pkg/api/user.go index bfdd36ab7b..c62613ccb7 100644 --- a/server/pkg/api/user.go +++ b/server/pkg/api/user.go @@ -82,6 +82,10 @@ func (h *UserHandler) SetAttributes(c *gin.Context) { handler.Error(c, stacktrace.Propagate(err, "")) return } + if err := request.Validate(); err != nil { + handler.Error(c, stacktrace.Propagate(err, "")) + return + } err := h.UserController.SetAttributes(userID, request) if err != nil { handler.Error(c, stacktrace.Propagate(err, "")) diff --git a/server/pkg/repo/srp.go b/server/pkg/repo/srp.go index 9a8480e6ac..efb7c3bea2 100644 --- a/server/pkg/repo/srp.go +++ b/server/pkg/repo/srp.go @@ -135,6 +135,9 @@ func (repo *UserAuthRepository) InsertOrUpdateSRPAuthAndKeyAttr(ctx context.Cont return stacktrace.Propagate(err, "") } updateKeyAttr := *req.UpdateAttributes + if validErr := updateKeyAttr.Validate(); validErr != nil { + return stacktrace.Propagate(validErr, "") + } _, err = tx.ExecContext(ctx, `UPDATE key_attributes SET kek_salt = $1, encrypted_key = $2, key_decryption_nonce = $3, mem_limit = $4, ops_limit = $5 WHERE user_id = $6`, updateKeyAttr.KEKSalt, updateKeyAttr.EncryptedKey, updateKeyAttr.KeyDecryptionNonce, updateKeyAttr.MemLimit, updateKeyAttr.OpsLimit, userID) if err != nil {