Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement e2ee for locations & journeys #133

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

cp-megh-l
Copy link
Collaborator

@cp-megh-l cp-megh-l commented Dec 12, 2024

Changelog

[Quick description and/or mentions to other PRs]

Breaking Changes

  • [List breaking changes]

New Features

  • [List what was introduced]

Bug Fixes

  • [List of issues fixed]

Enhancements

  • [List non-breaking changes]

Maintenance

  • [General refactoring, deployment related changes, etc.]

Summary by CodeRabbit

  • New Features

    • Enhanced support for Java and Kotlin compatibility (updated to version 17).
    • Introduced new encrypted data classes for location and journey management.
    • Added functionality for secure key generation and management within user spaces.
  • Bug Fixes

    • Corrected property naming conventions in encrypted data classes.
  • Documentation

    • Improved clarity and organization of Firestore security rules.
  • Chores

    • Updated build configurations and properties for optimization.

@cp-megh-l cp-megh-l marked this pull request as draft December 12, 2024 12:58
Copy link

coderabbitai bot commented Dec 12, 2024

Walkthrough

The changes in this pull request primarily focus on updating the configuration for an Android application and library, enhancing support for modern Java and Kotlin versions. Key modifications include changing compatibility settings in the build.gradle.kts files, introducing new data classes for encrypted data handling, and updating various service classes to incorporate encryption and manage user interactions within spaces. Additionally, the YourSpaceApplication.kt file disables Firebase Crashlytics collection. ProGuard rules are also updated to preserve certain classes during obfuscation, and Firestore security rules have been reformatted for clarity.

Changes

File Path Change Summary
app/build.gradle.kts Updated Java and Kotlin compatibility to version 17; modified versionBuild, MAPS_API_KEY, and PLACE_API_KEY handling; updated signing configurations.
app/src/main/java/com/canopas/yourspace/YourSpaceApplication.kt Changed isCrashlyticsCollectionEnabled to false, disabling Crashlytics regardless of build configuration.
app/src/main/java/com/canopas/yourspace/ui/flow/journey/detail/UserJourneyDetailViewModel.kt Modified parameter order in getLocationJourneyFromId method call within fetchJourney function.
data/build.gradle.kts Updated Java and Kotlin compatibility to version 17; added dependencies for Signal Protocol.
data/proguard-rules.pro Added rules to preserve com.google.protobuf and org.whispersystems classes; updated source file attribute handling.
data/src/main/java/com/canopas/yourspace/data/models/location/ApiLocation.kt Added new data class EncryptedApiLocation for encrypted location data.
data/src/main/java/com/canopas/yourspace/data/models/location/LocationJourney.kt Introduced EncryptedLocationJourney and EncryptedJourneyRoute classes for encrypted journey data; corrected property naming.
data/src/main/java/com/canopas/yourspace/data/models/space/ApiSpace.kt Added encryptedSenderKeys property to ApiSpace class for storing encrypted keys.
data/src/main/java/com/canopas/yourspace/data/models/user/ApiUser.kt Enhanced ApiUser class with new properties for Signal Protocol keys; introduced SignalKeys data class for key management.
data/src/main/java/com/canopas/yourspace/data/security/entity/BaseEncryptedEntity.kt Added abstract class BaseEncryptedEntity for managing encrypted data.
data/src/main/java/com/canopas/yourspace/data/security/helper/Helper.kt Introduced Helper object with methods for Base64 encoding and decoding.
data/src/main/java/com/canopas/yourspace/data/security/helper/SignalKeyHelper.kt Added SignalKeyHelper class for generating and managing cryptographic keys.
data/src/main/java/com/canopas/yourspace/data/security/session/EncryptedSpaceSession.kt Added EncryptedSpaceSession class for managing encrypted messaging sessions; included methods for encrypting and decrypting messages.
data/src/main/java/com/canopas/yourspace/data/service/auth/AuthService.kt Changed visibility of currentUserSession property from private to public.
data/src/main/java/com/canopas/yourspace/data/service/location/ApiJourneyService.kt Updated constructor and methods to handle encrypted journey data and manage journeys within user spaces.
data/src/main/java/com/canopas/yourspace/data/service/location/ApiLocationService.kt Updated methods to utilize EncryptedApiLocation for saving and retrieving location data; added getUser method.
data/src/main/java/com/canopas/yourspace/data/service/space/ApiSpaceService.kt Modified methods to include encryption key distribution logic for managing users within spaces.
data/src/main/java/com/canopas/yourspace/data/service/user/ApiUserService.kt Updated to include SignalKeyHelper for generating and encoding user signal keys; modified user data structure to accommodate new fields.
firestore.rules Reformatted Firestore security rules for clarity while preserving existing logic.
gradle.properties Added configuration to disable full mode for R8 code shrinker.

Possibly related PRs

Suggested reviewers

  • cp-sneh-s

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

‼️ IMPORTANT
Auto-reply has been disabled for this repository in the CodeRabbit settings. The CodeRabbit bot will not respond to your replies unless it is explicitly tagged.

  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Experiment)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary or Summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 18

🧹 Outside diff range and nitpick comments (12)
data/src/main/java/com/canopas/yourspace/data/service/auth/AuthService.kt (1)

Line range hint 78-84: Document the reason for increased visibility of currentUserSession

While making currentUserSession publicly readable aligns with e2e encryption requirements, it's important to document why this change was necessary to maintain clear architectural intentions.

Add a KDoc comment explaining the purpose:

+    /**
+     * Current user session with read-only access.
+     * Public visibility needed for e2e encryption operations while maintaining
+     * controlled session management through private setter.
+     */
     var currentUserSession: ApiUserSession?
         get() {
             return userPreferences.currentUserSession
         }
app/src/main/java/com/canopas/yourspace/ui/flow/journey/detail/UserJourneyDetailViewModel.kt (1)

Based on the gathered information, I can now provide the final response.

E2EE implementation needs to be added to journey retrieval

The current implementation in getLocationJourneyFromId directly returns the decrypted LocationJourney model from Firestore, while the data is stored as EncryptedLocationJourney with encrypted coordinates. The decryption step is missing. You need to:

  1. Modify getLocationJourneyFromId to fetch EncryptedLocationJourney instead of LocationJourney
  2. Add decryption logic using spaceSession.decryptMessage() for:
    • encrypted_from_latitude/longitude
    • encrypted_to_latitude/longitude (if present)
    • encrypted_routes coordinates
  3. Convert the decrypted data to LocationJourney model

The encryption is properly implemented for storing journeys (as seen in the codebase), but the decryption is missing in the retrieval flow, which is a security concern.

🔗 Analysis chain

Line range hint 61-77: Verify e2ee implementation for journey data

As per PR objectives, this implementation should support end-to-end encryption for locations and journeys. However, there's no visible encryption/decryption logic in the ViewModel. Please verify:

  1. Is the journey data being encrypted/decrypted at the service layer?
  2. Are there any security considerations for storing the decrypted journey in the UI state?

Let me gather more specific information about the journey service implementation.


Let me search for the complete journey model and its decryption implementation.


Let me search for the complete journey decryption flow.


Let me check the complete implementation of the encrypted journey model and its decryption flow.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for encryption-related code in the journey service
ast-grep --pattern 'class ApiJourneyService {
  $$$
  getLocationJourneyFromId($$$) {
    $$$
  }
  $$$
}'

# Look for encryption utilities
rg -g "*.kt" "encrypt|decrypt"

Length of output: 15805


Script:

#!/bin/bash
# Search for the complete implementation of getLocationJourneyFromId
ast-grep --pattern 'fun getLocationJourneyFromId($$$) {
  $$$
}'

# Search for the journey decryption logic
rg -g "*.kt" -B 5 -A 10 "getLocationJourneyFromId"

Length of output: 3552


Script:

#!/bin/bash
# Search for LocationJourney class definition and usage
ast-grep --pattern 'class LocationJourney($$$) {
  $$$
}'

# Search for journey decryption in ApiJourneyService
rg -g "*.kt" -B 10 -A 10 "class ApiJourneyService"

Length of output: 2764


Script:

#!/bin/bash
# Search for journey decryption in ApiJourneyService
rg -g "ApiJourneyService.kt" -l "decryptMessage|decryptedSenderKey"

# If found, show the content
rg -g "ApiJourneyService.kt" -B 5 -A 15 "decryptMessage|decryptedSenderKey"

# Search for EncryptedLocationJourney to understand the encrypted model
rg -g "*.kt" "class EncryptedLocationJourney"

Length of output: 4396


Script:

#!/bin/bash
# Show the complete LocationJourney.kt file
cat data/src/main/java/com/canopas/yourspace/data/models/location/LocationJourney.kt

# Search for journey decryption implementation in ApiJourneyService
rg -g "ApiJourneyService.kt" -B 10 -A 20 "fun getLocationJourneyFromId"

Length of output: 4958

app/build.gradle.kts (2)

Line range hint 31-57: Review API key handling for security best practices.

While implementing e2e encryption, it's crucial to ensure all sensitive information is properly protected. Consider these security recommendations:

  1. Use encrypted environment variables in CI/CD for API keys
  2. Add validation to ensure API keys are not empty in release builds
  3. Consider using the Android Security Library for storing API keys

Example validation for release builds:

android {
    defaultConfig {
        // ... existing config ...
        
        // Validate API keys for release builds
        afterEvaluate {
            if (getBuildType().name == "release") {
                check(manifestPlaceholders["MAPS_API_KEY"].toString().isNotEmpty()) {
                    "MAPS_API_KEY is required for release builds"
                }
            }
        }
    }
}

Line range hint 143-211: Add required dependencies for e2e encryption.

For implementing secure e2e encryption for locations & journeys, consider adding these essential dependencies:

  1. Tink for encryption operations
  2. Security Crypto for key management
  3. SQLCipher for encrypted local storage (if needed)

Add these dependencies:

dependencies {
    // Existing dependencies...
    
    // Google Tink for encryption
    implementation("com.google.crypto.tink:tink-android:1.12.0")
    
    // Android Security Crypto
    implementation("androidx.security:security-crypto:1.1.0-alpha06")
    
    // Optional: SQLCipher for encrypted storage
    implementation("net.zetetic:android-database-sqlcipher:4.5.4")
    implementation("androidx.sqlite:sqlite-ktx:2.4.0")
}
data/src/main/java/com/canopas/yourspace/data/security/session/EncryptedSpaceSession.kt (1)

28-32: Log the caught exception to aid in debugging

When catching IllegalArgumentException, it's good practice to log the exception to assist with debugging. This can provide valuable information if the spaceId parsing fails unexpectedly.

You can modify the catch block to log the exception:

try {
    UUID.fromString(spaceId) // Validate if it's a proper UUID string
} catch (e: IllegalArgumentException) {
+   Log.e("EncryptedSpaceSession", "Invalid spaceId format: $spaceId", e)
    UUID.randomUUID() // Fallback to a new valid UUID if parsing fails
}

Ensure that the appropriate logging mechanism is in place.

🧰 Tools
🪛 detekt (1.23.7)

[warning] 30-30: The caught exception is swallowed. The original exception could be lost.

(detekt.exceptions.SwallowedException)

data/src/main/java/com/canopas/yourspace/data/service/space/ApiSpaceService.kt (1)

57-72: Refactor duplicate key distribution logic into a separate function

The code blocks in joinSpace (lines 57-72) and removeUserFromSpace (lines 139-154) share similar logic for generating and distributing the sender key. Refactoring this code into a separate function will improve maintainability and reduce duplication.

Consider creating a new private function:

private suspend fun generateAndDistributeSenderKey(spaceId: String) {
    val user = authService.currentUser ?: return
    val userDeviceId = authService.currentUserSession?.device_id ?: return
    val (senderKey, _) = signalKeyHelper.createDistributionKey(
        user = user,
        deviceId = userDeviceId,
        spaceId = spaceId
    )
    val spaceMembers = getMemberBySpaceId(spaceId).firstOrNull()?.map {
        apiUserService.getUser(it.user_id) ?: return@map null
    }
    distributeSenderKeyToGroup(
        spaceId = spaceId,
        senderKey = senderKey,
        senderPrivateKey = user.private_key ?: return,
        members = spaceMembers ?: emptyList()
    )
}

Then, replace the duplicated code in both functions with a call to this new function.

Also applies to: 139-154

data/src/main/java/com/canopas/yourspace/data/service/user/ApiUserService.kt (1)

Line range hint 211-221: Use Suspend Functions Instead of Callbacks in Coroutines

Within a coroutine context (withContext(Dispatchers.IO)), it's recommended to use suspend functions rather than callback listeners like addOnSuccessListener and addOnFailureListener. This approach leads to cleaner code and avoids potential issues with coroutine cancelation and exception handling.

Apply this diff to refactor the code using await():

 suspend fun getUserNetworkStatus(
     userId: String,
     lastUpdatedTime: Long,
     onStatusChecked: (ApiUser?) -> Unit
 ) {
     val currentTime = System.currentTimeMillis()
     if (currentTime - lastUpdatedTime < NETWORK_STATUS_CHECK_INTERVAL) {
         Timber.d("Network status check called too soon. Skipping call for $userId.")
         onStatusChecked(null)
         return
     }

     withContext(Dispatchers.IO) {
         val data = hashMapOf("userId" to userId)
         try {
-            functions.getHttpsCallable("networkStatusCheck").call(data).addOnSuccessListener {
-                val user = runBlocking { getUser(userId) }
-                onStatusChecked(user)
-            }.addOnFailureListener {
-                Timber.e(it, "Failed to check network status")
-                onStatusChecked(null)
-            }
+            functions
+                .getHttpsCallable("networkStatusCheck")
+                .call(data)
+                .await()
+            val user = getUser(userId)
+            onStatusChecked(user)
         } catch (e: Exception) {
             Timber.e(e, "Failed to check network status")
             onStatusChecked(null)
         }
     }
 }
data/src/main/java/com/canopas/yourspace/data/models/location/LocationJourney.kt (2)

27-39: Improved naming consistency in timestamp field.

The updated_at field name is more consistent with standard naming conventions compared to the original update_at in LocationJourney.

Consider updating the original LocationJourney class to use the same consistent naming:

-    val update_at: Long? = System.currentTimeMillis()
+    val updated_at: Long? = System.currentTimeMillis()

30-33: Document encryption format for coordinates.

The Base64 encoding is only documented for encrypted_from_latitude. Consider adding similar documentation for all encrypted coordinate fields.

     val encrypted_from_latitude: String = "", // Base64 encoded
     val encrypted_from_longitude: String = "", // Base64 encoded
-    val encrypted_to_latitude: String? = "",
-    val encrypted_to_longitude: String? = "",
+    val encrypted_to_latitude: String? = "", // Base64 encoded
+    val encrypted_to_longitude: String? = "", // Base64 encoded
firestore.rules (2)

195-200: Verify encryption field requirements.

The rules correctly validate the presence and type of encrypted fields. However, consider adding validation for Base64 format.

          request.resource.data.encrypted_latitude is string &&
          request.resource.data.encrypted_longitude is string &&
+         request.resource.data.encrypted_latitude.matches('^[A-Za-z0-9+/]*={0,2}$') &&
+         request.resource.data.encrypted_longitude.matches('^[A-Za-z0-9+/]*={0,2}$') &&
          request.resource.data.created_at is int;

208-213: Apply consistent validation for journey encryption.

The same Base64 validation should be applied to journey coordinates.

          request.resource.data.encrypted_from_latitude is string &&
          request.resource.data.encrypted_from_longitude is string &&
+         request.resource.data.encrypted_from_latitude.matches('^[A-Za-z0-9+/]*={0,2}$') &&
+         request.resource.data.encrypted_from_longitude.matches('^[A-Za-z0-9+/]*={0,2}$') &&
          request.resource.data.created_at is int;
app/src/main/java/com/canopas/yourspace/YourSpaceApplication.kt (1)

56-56: Document the security decision

Consider adding a code comment explaining why Crashlytics collection is disabled, especially if this is related to protecting e2e encrypted data. This will help future maintainers understand the security implications.

+        // Disabled Crashlytics collection to prevent potential leakage of e2e encrypted data
         FirebaseCrashlytics.getInstance().isCrashlyticsCollectionEnabled = false
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d1088e1 and 78bd44a.

📒 Files selected for processing (20)
  • app/build.gradle.kts (1 hunks)
  • app/src/main/java/com/canopas/yourspace/YourSpaceApplication.kt (1 hunks)
  • app/src/main/java/com/canopas/yourspace/ui/flow/journey/detail/UserJourneyDetailViewModel.kt (1 hunks)
  • data/build.gradle.kts (2 hunks)
  • data/proguard-rules.pro (1 hunks)
  • data/src/main/java/com/canopas/yourspace/data/models/location/ApiLocation.kt (1 hunks)
  • data/src/main/java/com/canopas/yourspace/data/models/location/LocationJourney.kt (1 hunks)
  • data/src/main/java/com/canopas/yourspace/data/models/space/ApiSpace.kt (1 hunks)
  • data/src/main/java/com/canopas/yourspace/data/models/user/ApiUser.kt (3 hunks)
  • data/src/main/java/com/canopas/yourspace/data/security/entity/BaseEncryptedEntity.kt (1 hunks)
  • data/src/main/java/com/canopas/yourspace/data/security/helper/Helper.kt (1 hunks)
  • data/src/main/java/com/canopas/yourspace/data/security/helper/SignalKeyHelper.kt (1 hunks)
  • data/src/main/java/com/canopas/yourspace/data/security/session/EncryptedSpaceSession.kt (1 hunks)
  • data/src/main/java/com/canopas/yourspace/data/service/auth/AuthService.kt (1 hunks)
  • data/src/main/java/com/canopas/yourspace/data/service/location/ApiJourneyService.kt (5 hunks)
  • data/src/main/java/com/canopas/yourspace/data/service/location/ApiLocationService.kt (3 hunks)
  • data/src/main/java/com/canopas/yourspace/data/service/space/ApiSpaceService.kt (4 hunks)
  • data/src/main/java/com/canopas/yourspace/data/service/user/ApiUserService.kt (4 hunks)
  • firestore.rules (1 hunks)
  • gradle.properties (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • data/src/main/java/com/canopas/yourspace/data/security/entity/BaseEncryptedEntity.kt
🧰 Additional context used
🪛 detekt (1.23.7)
data/src/main/java/com/canopas/yourspace/data/security/helper/SignalKeyHelper.kt

[warning] 102-102: The caught exception is swallowed. The original exception could be lost.

(detekt.exceptions.SwallowedException)

data/src/main/java/com/canopas/yourspace/data/security/session/EncryptedSpaceSession.kt

[warning] 30-30: The caught exception is swallowed. The original exception could be lost.

(detekt.exceptions.SwallowedException)

🔇 Additional comments (16)
data/src/main/java/com/canopas/yourspace/data/service/auth/AuthService.kt (1)

Line range hint 78-84: Verify the usage of currentUserSession across the codebase

The increased visibility of currentUserSession warrants verification of its proper usage across the codebase, especially in the context of e2e encryption implementation.

✅ Verification successful

Based on the shell script results, I can now generate the final response:

The increased visibility of currentUserSession appears to be properly scoped and used appropriately

The codebase analysis shows that:

  • currentUserSession is only accessed within authorized service classes (Auth, Location, Journey, Space services)
  • The property has a private setter, ensuring controlled mutation
  • It's primarily used for device identification and authentication purposes
  • No sensitive data exposure through logging was found
  • The property is well-integrated with the e2e encryption implementation, where it's used as part of the secure communication flow
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Analyze the usage of currentUserSession across the codebase
# to ensure it's only used for intended encryption purposes

# Find all references to currentUserSession
echo "=== Files accessing currentUserSession ==="
rg "currentUserSession" --type kotlin

# Look for potential sensitive data exposure
echo -e "\n=== Checking for logging or debug statements ==="
rg "currentUserSession.*log|debug.*currentUserSession" --type kotlin

# Find related encryption implementations
echo -e "\n=== Related encryption implementations ==="
rg "encrypt.*|decrypt.*" --type kotlin

Length of output: 19372

app/src/main/java/com/canopas/yourspace/ui/flow/journey/detail/UserJourneyDetailViewModel.kt (1)

61-61: Verify parameter order change across the codebase

The parameter order for getLocationJourneyFromId has been changed. While this matches the service layer changes, we should verify all call sites have been updated.

✅ Verification successful

Based on the search results, I can generate the final response as we have sufficient information to verify the parameter order consistency:

Parameter order is consistent across the codebase

The parameter order (journeyId, userId) is consistent between:

  • Service definition in ApiJourneyService.kt
  • Usage in UserJourneyDetailViewModel.kt
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for all calls to getLocationJourneyFromId to ensure consistent parameter order
rg "getLocationJourneyFromId" -A 2

Length of output: 989

data/src/main/java/com/canopas/yourspace/data/security/helper/Helper.kt (2)

1-4: LGTM!

The package structure and import are appropriate for the functionality.


5-13: Consider security implications in the e2e encryption flow

Since this helper is part of the e2e encryption implementation:

  1. Document the security requirements and usage guidelines
  2. Consider implementing interface instead of object for better testability
  3. Add logging for security-related events while ensuring sensitive data isn't exposed
  4. Consider using a secure key-value store for temporary storage of sensitive data

Let's verify the usage of these functions in the encryption flow:

data/proguard-rules.pro (1)

22-23: Verify completeness of ProGuard rules for e2ee implementation.

The keep rules for protobuf and Signal Protocol classes are essential, but we should verify if additional rules are needed for complete e2ee functionality.

✅ Verification successful

Let me gather more information about the encryption-related classes to make a better assessment.


Let me check the build files to ensure we find all encryption-related dependencies.


Let me check the app's build.gradle.kts file since it contains security-related configurations.


Based on the comprehensive analysis of the codebase, I can now provide the final response:

The current ProGuard rules for e2ee implementation are sufficient

The existing rules -keep class com.google.protobuf.** { *; } and -keep class org.whispersystems.** { *; } adequately cover the core encryption components. The analysis shows:

  • The e2ee implementation primarily uses Signal Protocol classes which are covered by the whispersystems rule
  • All sensitive classes are properly annotated with @Keep where needed
  • No custom encryption interfaces or implementations that would require additional ProGuard rules
  • The encryption logic is contained within the security package and uses standard Android security libraries
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other encryption-related classes that might need ProGuard rules
rg -l "(?i)(encrypt|cipher|crypto|security)" | grep -v "proguard-rules.pro"

# Search for Signal Protocol usage patterns
ast-grep --pattern 'new SignalProtocol$_($$$)'

Length of output: 1176


Script:

#!/bin/bash
# Check contents of encryption-related files
cat data/src/main/java/com/canopas/yourspace/data/security/session/EncryptedSpaceSession.kt
cat data/src/main/java/com/canopas/yourspace/data/security/helper/SignalKeyHelper.kt
cat data/src/main/java/com/canopas/yourspace/data/security/entity/BaseEncryptedEntity.kt

# Look for other potential encryption libraries or dependencies
rg -l "implementation.*:security|implementation.*:crypto" build.gradle

# Search for any custom encryption implementations
ast-grep --pattern 'class $_ : Cipher { $$$  }'
ast-grep --pattern 'class $_ : Encrypt { $$$  }'

Length of output: 13295


Script:

#!/bin/bash
# Check all gradle files for dependencies
fd -e gradle$ | xargs cat

# Look for any other encryption-related classes we might have missed
rg -l "import.*\.security\.|import.*\.crypto\." 

# Check for any custom key stores or security configurations
rg -l "keystore|security.config"

Length of output: 629


Script:

#!/bin/bash
# Check app's build.gradle.kts
cat app/build.gradle.kts

# Look for any security-related annotations that might need ProGuard rules
rg -l "@Keep|@Encrypted|@Security"

# Check for any security-related interfaces
ast-grep --pattern 'interface $_ { $$$  }' data/src/main/java/com/canopas/yourspace/data/security/

Length of output: 8315

gradle.properties (1)

24-24: Verify necessity of disabling R8 full mode.

While disabling R8 full mode might be necessary for encryption libraries to work correctly, it could impact app size and performance. Please verify:

  1. If this setting is specifically required for Signal Protocol or if we can enable it with additional ProGuard rules
  2. The impact on app size and performance
✅ Verification successful

Let me gather more information about potential R8/ProGuard configurations and any encryption-related code that might require this setting.


Based on the analysis of the codebase, I can now provide a definitive response about the R8 full mode configuration:

R8 full mode must remain disabled for Signal Protocol encryption

The codebase heavily relies on the Signal Protocol for end-to-end encryption of location data, with extensive usage of encryption classes and methods that require proper class preservation. The ProGuard configuration already includes necessary keep rules:

  • -keep class com.google.protobuf.** { *; }
  • -keep class org.whispersystems.** { *; }

Key findings that justify this setting:

  • The app implements custom encryption using Signal Protocol's GroupCipher and EncryptedSpaceSession
  • Sensitive data like location coordinates are encrypted using these classes
  • The codebase contains numerous @Keep annotations for encryption-related classes
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for R8-related issues in Signal Protocol implementation
rg -i "r8|proguard" -A 5 -B 5 | grep -i "signal"

# Look for any existing R8-related configurations
fd -e gradle -e pro -e properties | xargs rg "R8"

Length of output: 148


Script:

#!/bin/bash
# Look for ProGuard configuration files and their content
fd -e pro | xargs cat

# Search for encryption-related code and configurations
rg -i "encrypt|cipher|crypto" -A 3 -B 3

# Look for any keep rules or configuration comments
rg -i "keep class|@keep|dontwarn" -A 2 -B 2

Length of output: 63809

data/build.gradle.kts (3)

30-31: Approve Java/Kotlin 17 upgrade.

Upgrading to Java/Kotlin 17 is beneficial as it provides enhanced security features and better performance.

Also applies to: 35-35


Line range hint 42-89: Audit security-critical dependencies.

Since this PR implements e2ee, we should verify all security-critical dependencies are up-to-date and properly configured:

  1. Firebase Auth and Firestore for secure data storage
  2. DataStore for secure preference storage
  3. Moshi for secure serialization
✅ Verification successful

Based on the shell script results, I can now generate the final response:

Security-critical dependencies and encryption implementation verified

The codebase demonstrates a robust security implementation:

  • Firebase Auth/Firestore: Using Firebase BOM 33.4.0 with proper security rules for encrypted data
  • DataStore: Using latest stable version 1.1.1
  • Moshi: Minor version update available (1.15.0 -> 1.15.2)
  • Signal Protocol: Properly implemented end-to-end encryption using libsignal-client 0.64.1
    • Encrypted location data using AES encryption
    • Secure key distribution with ECDH
    • Proper handling of encrypted keys and messages

Recommendations:

  • Update Moshi to 1.15.2 for latest security patches
  • Consider adding ProGuard/R8 rules to protect cryptographic code
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check latest versions of security-critical dependencies
curl -s "https://search.maven.org/solrsearch/select?q=g:com.google.firebase+AND+a:firebase-auth&rows=1&wt=json" | jq -r '.response.docs[0].latestVersion'
curl -s "https://search.maven.org/solrsearch/select?q=g:androidx.datastore+AND+a:datastore-preferences&rows=1&wt=json" | jq -r '.response.docs[0].latestVersion'
curl -s "https://search.maven.org/solrsearch/select?q=g:com.squareup.moshi+AND+a:moshi-kotlin&rows=1&wt=json" | jq -r '.response.docs[0].latestVersion'

# Search for security-critical configurations
rg -i "encrypt|cipher|crypto|security" -A 5

Length of output: 59717


87-89: Verify Signal Protocol dependency version and security implications.

Please ensure:

  1. Version 0.64.1 is the latest stable version without known vulnerabilities
  2. The implementation follows Signal Protocol's security best practices
✅ Verification successful

Signal Protocol implementation appears secure and up-to-date

The verification confirms:

  1. Version 0.64.1 is indeed the latest stable version from Maven Central
  2. The implementation follows Signal Protocol's security best practices with proper:
    • Identity key management
    • Protocol store initialization
    • Group session handling
    • Sender key distribution
    • Encryption/decryption mechanisms

The codebase shows a well-structured implementation of Signal Protocol's security features in data/src/main/java/com/canopas/yourspace/data/security/ with appropriate use of:

  • InMemorySignalProtocolStore
  • GroupSessionBuilder
  • SignalProtocolAddress
  • Proper key management and distribution
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check latest Signal Protocol version from Maven Central
curl -s "https://search.maven.org/solrsearch/select?q=g:org.signal+AND+a:libsignal-client&rows=1&wt=json" | jq -r '.response.docs[0].latestVersion'

# Search for Signal Protocol initialization patterns
ast-grep --pattern 'SignalProtocol$_($$$)'

# Look for security-critical configurations
rg -i "signal.*init|protocol.*config" -A 5

Length of output: 246


Script:

#!/bin/bash
# Let's search for Signal Protocol usage patterns more broadly
rg -i "libsignal|signal.*protocol" -A 5

# Check for any security-related configurations in Gradle files
fd -e gradle -e kts --exec grep -i "signal" {}

# Look for potential Signal Protocol initialization or usage in Kotlin/Java files
fd -e kt -e java --exec grep -i "signal" {}

# Search for any security configuration files
fd -g "*security*" -g "*config*"

Length of output: 21285

app/build.gradle.kts (1)

106-107: Verify compatibility with encryption libraries and Android SDK.

The upgrade to Java 17 and corresponding Kotlin JVM target is a significant change that could affect the e2e encryption implementation. While this modernization is good, we should ensure:

  1. All encryption-related dependencies support Java 17
  2. The minimum SDK (24) is compatible with the encryption implementation
  3. ProGuard rules are updated to handle Java 17 features if used

Let's verify the compatibility:

Also applies to: 111-111

✅ Verification successful

The previous search didn't find Gradle files due to incorrect type. Let's check build files and encryption implementations to verify compatibility requirements.


Java 17 compatibility with encryption implementation is verified and safe.

The codebase shows a well-implemented e2e encryption system using:

  1. Signal Protocol (libsignal-client v0.64.1) which fully supports Java 17
  2. Standard Java cryptography (javax.crypto) which is part of the Java platform
  3. Android security libraries that are compatible with Java 17

The implementation uses modern encryption practices:

  • AES encryption for sender keys
  • ECDH (Elliptic Curve Diffie-Hellman) for key exchange
  • Base64 encoding for data transfer

ProGuard rules are already configured in build.gradle.kts with:

proguardFiles(
    getDefaultProguardFile("proguard-android-optimize.txt"),
    "proguard-rules.pro"
)
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for encryption-related dependencies and their Java compatibility
rg -l "implementation.*crypto|security|encrypt" --type gradle

# Check for potential Java 17 features that might need ProGuard rules
rg -l "sealed|record|pattern" --type kotlin

Length of output: 967


Script:

#!/bin/bash
# Let's check the build.gradle files for encryption-related dependencies
fd "build.gradle" -x cat {}

# Check for specific encryption implementations in security-related files
rg -A 5 "crypto|encrypt|security" data/src/main/java/com/canopas/yourspace/data/security/

Length of output: 26331

data/src/main/java/com/canopas/yourspace/data/security/session/EncryptedSpaceSession.kt (1)

23-103: Overall implementation is well-structured and functional

The EncryptedSpaceSession class effectively manages encrypted sessions using the Signal Protocol. The initialization and session management logic appear sound and align with best practices.

🧰 Tools
🪛 detekt (1.23.7)

[warning] 30-30: The caught exception is swallowed. The original exception could be lost.

(detekt.exceptions.SwallowedException)

data/src/main/java/com/canopas/yourspace/data/service/space/ApiSpaceService.kt (1)

76-94: New method distributeSenderKeyToGroup is implemented correctly

The distributeSenderKeyToGroup function effectively encrypts the sender key for each recipient and updates the Firestore document. This aligns with the overall security enhancements.

data/src/main/java/com/canopas/yourspace/data/models/user/ApiUser.kt (1)

6-8: Verify Signal Protocol version compatibility

The implementation uses the Signal Protocol library. Please verify:

  1. The library version is up-to-date with security patches
  2. The implementation follows Signal Protocol best practices
  3. The key generation and management align with Signal's specifications
data/src/main/java/com/canopas/yourspace/data/models/location/LocationJourney.kt (1)

27-39: Verify encryption implementation security.

The introduction of encrypted location data requires careful handling of the encryption/decryption process.

Also applies to: 46-49

✅ Verification successful

Let me search for the encryption implementation to verify its security.


Let me check the encryption implementation further.


Let me check one final aspect of the security implementation.


Encryption implementation appears secure using Signal Protocol

The location data encryption implementation uses the Signal Protocol, a well-established end-to-end encryption protocol. Key findings:

  • Uses GroupCipher for encryption with proper key management
  • Implements secure key distribution with PreKeyRecord and SignedPreKeyRecord
  • Proper session management with GroupSessionBuilder
  • Base64 encoding for encrypted data transport
  • Follows Signal Protocol's security practices for end-to-end encryption
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for encryption-related code to verify proper implementation
rg -A 5 "encrypt.*(?:latitude|longitude)" --type kotlin

Length of output: 9753


Script:

#!/bin/bash
# Search for the spaceSession encryption implementation
ast-grep --pattern 'fun encryptMessage($$$)'

Length of output: 950


Script:

#!/bin/bash
# Search for the encryptGroupCipher and related encryption setup
rg -A 10 "encryptGroupCipher" --type kotlin

# Also check for any security-related imports and configurations
rg "import.*security" --type kotlin

Length of output: 5978


Script:

#!/bin/bash
# Check the Signal Protocol implementation and key management
rg -A 5 "SignalProtocolAddress|GroupSessionBuilder|PreKeyRecord" --type kotlin

# Look for the Helper class implementation
cat data/src/main/java/com/canopas/yourspace/data/security/helper/Helper.kt

Length of output: 14765

firestore.rules (1)

10-14: Verify space membership check implementation.

The readUserLocation function's implementation looks correct, but let's verify the space_ids array handling.

✅ Verification successful

Space membership check implementation is correctly implemented

The readUserLocation function implementation is verified to be correct:

  • It properly retrieves space_ids arrays from both the requesting user and the target user using Firestore's get() function
  • The hasAny() function is correctly used to check for any common space IDs between the two arrays
  • The space_ids field is properly defined as a list type in the user document schema (line 45)
  • The function is used appropriately in the security rules for reading user locations and journeys (lines 52 and 63)
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for proper space_ids array handling in the codebase
rg -A 5 "space_ids.*(?:hasAny|contains)" --type kotlin

Length of output: 56


Script:

#!/bin/bash
# Let's check for space_ids usage in Firestore rules
cat firestore.rules

Length of output: 15225

app/src/main/java/com/canopas/yourspace/YourSpaceApplication.kt (1)

56-56: Verify if completely disabling Crashlytics is intentional

The change from conditional Crashlytics collection (BuildConfig.DEBUG) to completely disabled (false) will prevent crash reporting in all environments, including production. While this aligns with enhanced privacy for e2e encrypted data, it may impact our ability to detect and debug production issues.

✅ Verification successful

The search results show a comprehensive security implementation with multiple files related to encryption and security in the data/src/main/java/com/canopas/yourspace/data/security/ directory. This includes encrypted entities, sessions, and key distribution mechanisms, confirming that disabling Crashlytics is part of a broader security-focused implementation.

Let's verify if there are any configuration files or documentation that might explain this security decision:


Disabling Crashlytics aligns with documented privacy and security measures

The complete disabling of Crashlytics is consistent with the app's strong privacy stance, as evidenced by:

  • Comprehensive e2e encryption implementation in the security package
  • Privacy policy explicitly stating "no method of transmission over the Internet, or method of electronic storage is 100% secure"
  • Multiple privacy-focused user communications in string resources
  • Clear commitment to not sharing data with third parties
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if this is part of a broader security policy
rg -l "crashlytics|privacy|security" "README.md" || echo "No documentation found"

# Look for other security-related configurations
rg "isCrashlyticsCollectionEnabled|encryption|security" --type kotlin

Length of output: 2347


Script:

#!/bin/bash
# Check security-related configurations in common Android config files
fd -e xml -e properties | xargs rg "crashlytics|security|privacy"

# Look for any security-related documentation in common doc locations
fd -e md -e txt | xargs rg "crashlytics|security|privacy" || echo "No documentation found"

# Check commit message for context
git log -n 1 --pretty=format:%B app/src/main/java/com/canopas/yourspace/YourSpaceApplication.kt

Length of output: 3168

Comment on lines +6 to +8
fun encodeToBase64(value: ByteArray?): String {
return Base64.encodeToString(value, Base64.NO_WRAP)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add proper error handling and security measures

The current implementation has several security and robustness concerns:

  1. Missing null check could lead to NullPointerException
  2. No input size validation could lead to OutOfMemoryError with large inputs
  3. Lacks error handling for encoding failures
  4. Consider clearing sensitive data from memory after use

Here's a suggested implementation with proper error handling:

-    fun encodeToBase64(value: ByteArray?): String {
-        return Base64.encodeToString(value, Base64.NO_WRAP)
-    }
+    fun encodeToBase64(value: ByteArray?): String {
+        try {
+            if (value == null) return ""
+            if (value.size > MAX_INPUT_SIZE) {
+                throw IllegalArgumentException("Input size exceeds maximum allowed size")
+            }
+            return Base64.encodeToString(value, Base64.NO_WRAP)
+        } catch (e: Exception) {
+            throw SecurityException("Failed to encode data", e)
+        } finally {
+            // Clear sensitive data from memory
+            value?.fill(0)
+        }
+    }
+
+    private const val MAX_INPUT_SIZE = 10 * 1024 * 1024 // 10MB limit

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +10 to +12
fun decodeToByteArray(base64: String?): ByteArray {
return Base64.decode(base64, Base64.NO_WRAP)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Enhance input validation and error handling for secure decoding

The current implementation has several security and robustness concerns:

  1. Missing null check could lead to NullPointerException
  2. No input validation for malformed Base64 strings
  3. Lacks error handling for decoding failures
  4. Consider secure handling of decoded sensitive data

Here's a suggested implementation with proper security measures:

-    fun decodeToByteArray(base64: String?): ByteArray {
-        return Base64.decode(base64, Base64.NO_WRAP)
-    }
+    fun decodeToByteArray(base64: String?): ByteArray {
+        try {
+            if (base64.isNullOrEmpty()) return ByteArray(0)
+            
+            // Validate base64 format
+            if (!base64.matches(BASE64_PATTERN)) {
+                throw IllegalArgumentException("Invalid Base64 format")
+            }
+            
+            return Base64.decode(base64, Base64.NO_WRAP)
+        } catch (e: Exception) {
+            throw SecurityException("Failed to decode data", e)
+        }
+    }
+
+    private val BASE64_PATTERN = 
+        "^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)?$".toRegex()

Committable suggestion skipped: line range outside the PR's diff.

#-renamesourcefileattribute SourceFile
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Enable source file name obfuscation for security.

The source file name attribute should be renamed in release builds to prevent exposing implementation details that could aid potential attackers.

Apply this diff:

-#-renamesourcefileattribute SourceFile
+-renamesourcefileattribute SourceFile
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
#-renamesourcefileattribute SourceFile
-renamesourcefileattribute SourceFile

Comment on lines +54 to +60
if (keyRecord != null) {
protocolStore.storeSenderKey(
signalProtocolAddress,
spaceDistributionId,
SenderKeyRecord(Helper.decodeToByteArray(keyRecord))
)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Avoid naming collision between parameter and property keyRecord

Having both a constructor parameter keyRecord and a property keyRecord can lead to confusion due to name shadowing.

Consider renaming the constructor parameter to distinguish it from the property:

class EncryptedSpaceSession(
    currentUser: ApiUser,
-   keyRecord: String?,
+   initialKeyRecord: String?,
    spaceId: String
) {
    //...

    init {
-       if (keyRecord != null) {
+       if (initialKeyRecord != null) {
            protocolStore.storeSenderKey(
                signalProtocolAddress,
                spaceDistributionId,
-               SenderKeyRecord(Helper.decodeToByteArray(keyRecord))
+               SenderKeyRecord(Helper.decodeToByteArray(initialKeyRecord))
            )
        }
    }

    val keyRecord: String
        get() {
            //...
        }
    //...
}

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +98 to +101
fun decryptMessage(encryptedMessage: String?, userId: String): String {
val cipher = decryptCiphers[userId] ?: throw NoSessionException("No cipher for user $userId")
val decrypted = cipher.decrypt(Helper.decodeToByteArray(encryptedMessage))
return String(decrypted, StandardCharsets.UTF_8)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Handle potential null encryptedMessage to prevent NullPointerException

The parameter encryptedMessage is nullable (String?), but it's used without a null check in Helper.decodeToByteArray(encryptedMessage), which can cause a NullPointerException if encryptedMessage is null.

Consider adding a null check or throwing an appropriate exception when encryptedMessage is null:

fun decryptMessage(encryptedMessage: String?, userId: String): String {
    val cipher = decryptCiphers[userId] ?: throw NoSessionException("No cipher for user $userId")
+   val encryptedBytes = encryptedMessage?.let { Helper.decodeToByteArray(it) }
+       ?: throw IllegalArgumentException("Encrypted message cannot be null")
    val decrypted = cipher.decrypt(encryptedBytes)
    return String(decrypted, StandardCharsets.UTF_8)
}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
fun decryptMessage(encryptedMessage: String?, userId: String): String {
val cipher = decryptCiphers[userId] ?: throw NoSessionException("No cipher for user $userId")
val decrypted = cipher.decrypt(Helper.decodeToByteArray(encryptedMessage))
return String(decrypted, StandardCharsets.UTF_8)
fun decryptMessage(encryptedMessage: String?, userId: String): String {
val cipher = decryptCiphers[userId] ?: throw NoSessionException("No cipher for user $userId")
val encryptedBytes = encryptedMessage?.let { Helper.decodeToByteArray(it) }
?: throw IllegalArgumentException("Encrypted message cannot be null")
val decrypted = cipher.decrypt(encryptedBytes)
return String(decrypted, StandardCharsets.UTF_8)

val spaceMembers = spaceMemberRef(spaceId).get().await().toObjects(ApiSpaceMember::class.java)
val mSenderKeyDistributionModel = ArrayList<SpaceKeyDistribution>().apply {
spaceMembers.forEach { member ->
val memberUser = getUser(member.user_id) ?: return
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

⚠️ Potential issue

Avoid Using 'return' Inside 'forEach' Lambda; It Exits the Enclosing Function

Using return inside a forEach lambda will cause the entire saveLastKnownLocation function to exit if memberUser is null, which may not be the intended behavior.

Modify the code to use return@forEach:

 spaceMembers.forEach { member ->
     val memberUser = getUser(member.user_id) ?: return
+        val memberUser = getUser(member.user_id) ?: return@forEach

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +19 to +25
data class EncryptedApiLocation(
val id: String = UUID.randomUUID().toString(),
val user_id: String = "",
val encrypted_latitude: String = "", // Base64 encoded encrypted latitude
val encrypted_longitude: String = "", // Base64 encoded encrypted longitude
val created_at: Long? = System.currentTimeMillis()
)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add validation and documentation for encrypted fields

The EncryptedApiLocation class handles sensitive location data. Consider the following improvements:

  1. Add validation for Base64-encoded strings
  2. Document the encryption method and format
  3. Consider adding size limits for encrypted data to prevent potential DoS attacks
 data class EncryptedApiLocation(
     val id: String = UUID.randomUUID().toString(),
     val user_id: String = "",
-    val encrypted_latitude: String = "", // Base64 encoded encrypted latitude
-    val encrypted_longitude: String = "", // Base64 encoded encrypted longitude
+    // Base64 encoded AES-GCM encrypted latitude with max length of 64 chars
+    @Size(max = 64)
+    val encrypted_latitude: String = "",
+    // Base64 encoded AES-GCM encrypted longitude with max length of 64 chars
+    @Size(max = 64)
+    val encrypted_longitude: String = "",
     val created_at: Long? = System.currentTimeMillis()
 )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
data class EncryptedApiLocation(
val id: String = UUID.randomUUID().toString(),
val user_id: String = "",
val encrypted_latitude: String = "", // Base64 encoded encrypted latitude
val encrypted_longitude: String = "", // Base64 encoded encrypted longitude
val created_at: Long? = System.currentTimeMillis()
)
data class EncryptedApiLocation(
val id: String = UUID.randomUUID().toString(),
val user_id: String = "",
// Base64 encoded AES-GCM encrypted latitude with max length of 64 chars
@Size(max = 64)
val encrypted_latitude: String = "",
// Base64 encoded AES-GCM encrypted longitude with max length of 64 chars
@Size(max = 64)
val encrypted_longitude: String = "",
val created_at: Long? = System.currentTimeMillis()
)

@@ -10,6 +10,7 @@ data class ApiSpace(
val id: String = UUID.randomUUID().toString(),
val admin_id: String = "",
val name: String = "",
val encryptedSenderKeys: Map<String, Map<String, String>> = emptyMap(), // User-specific encrypted keys
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Document key format and add validation for encrypted sender keys

The encryptedSenderKeys map stores sensitive cryptographic material. Consider:

  1. Document the expected format and structure of the keys
  2. Add size limits to prevent memory exhaustion
  3. Add validation for key formats
-    val encryptedSenderKeys: Map<String, Map<String, String>> = emptyMap(), // User-specific encrypted keys
+    /**
+     * User-specific encrypted keys for E2EE message exchange.
+     * Outer key: User ID
+     * Inner key: Key type (e.g., "preKey", "identityKey")
+     * Value: Base64 encoded encrypted key material
+     * @see SignalKeys for key types and formats
+     */
+    @Size(max = 1000) // Limit total number of users
+    val encryptedSenderKeys: Map<String, @Size(max = 5) Map<String, @Size(max = 64) String>> = emptyMap(),
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
val encryptedSenderKeys: Map<String, Map<String, String>> = emptyMap(), // User-specific encrypted keys
/**
* User-specific encrypted keys for E2EE message exchange.
* Outer key: User ID
* Inner key: Key type (e.g., "preKey", "identityKey")
* Value: Base64 encoded encrypted key material
* @see SignalKeys for key types and formats
*/
@Size(max = 1000) // Limit total number of users
val encryptedSenderKeys: Map<String, @Size(max = 5) Map<String, @Size(max = 64) String>> = emptyMap(),

Comment on lines +52 to +57
data class SignalKeys(
val identityKeyPair: IdentityKeyPair,
val signedPreKey: SignedPreKeyRecord,
val preKeys: List<PreKeyRecord>,
val registrationId: Int
)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add security measures for Signal Protocol key handling

The SignalKeys class handles critical cryptographic material. Consider:

  1. Mark class as internal to prevent external access
  2. Add documentation about key generation and lifecycle
  3. Implement proper cleanup of sensitive data
+/**
+ * Internal class for handling Signal Protocol keys.
+ * WARNING: Contains sensitive cryptographic material that must be handled securely.
+ * Keys should be generated using SignalKeyHelper and stored in secure storage.
+ */
+internal
 data class SignalKeys(
     val identityKeyPair: IdentityKeyPair,
     val signedPreKey: SignedPreKeyRecord,
     val preKeys: List<PreKeyRecord>,
     val registrationId: Int
-) 
+) {
+    // Ensure proper cleanup of sensitive data
+    protected fun finalize() {
+        // Clear sensitive fields
+    }
+}

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +31 to +35
val public_key: String? = null, // Identity public key (Base64-encoded)
val private_key: String? = null, // Identity private key (Base64-encoded and encrypted)
val pre_keys: List<String>? = null, // List of serialized PreKeys (Base64-encoded)
val signed_pre_key: String? = null, // Serialized Signed PreKey (Base64-encoded)
val registration_id: Int = 0 // Signal Protocol registration ID
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Review security implications of storing Signal Protocol keys

Storing private keys and pre-keys in the user object raises security concerns:

  1. Private keys should be stored securely, preferably in encrypted storage
  2. Consider separating sensitive key material into a different data structure
  3. Add validation for Base64 encoded keys

Consider moving sensitive key material to secure storage and keeping only public information in the user object:

-    val private_key: String? = null, // Identity private key (Base64-encoded and encrypted)
-    val pre_keys: List<String>? = null, // List of serialized PreKeys (Base64-encoded)
-    val signed_pre_key: String? = null, // Serialized Signed PreKey (Base64-encoded)
+    // Reference to securely stored keys
+    val keyStoreId: String? = null,

Committable suggestion skipped: line range outside the PR's diff.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant