From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Samuel Attard <sattard@salesforce.com>
Date: Thu, 29 Sep 2022 16:58:47 -0700
Subject: feat: ensure mas builds of the same application can use safestorage

This change ensures that MAS builds of applications with an equivilant darwin build that share the same name do not fight over access to the same Safe Storage account.

Specifically this changes the account name for app "My App" from "My App" to "My App AppStore" if the app is using a MAS build of Electron.

We attempt to migrate the safe storage key from the old account, if that migration succeeds we delete the old key and move on.

Existing apps that aren't built for the app store should be unimpacted, there is one edge case where a user uses BOTH an AppStore and a darwin build of the same app only one will keep it's access to the safestorage key as during the migration we delete the old account.  This is an acceptable edge case as no one should be actively using two versions of the same app.

diff --git a/components/os_crypt/common/keychain_password_mac.mm b/components/os_crypt/common/keychain_password_mac.mm
index 944f58043b9f634e79c9a9840a39c26966560f02..13a6a5465bca3cc6e0024c1b1206ba35263efdff 100644
--- a/components/os_crypt/common/keychain_password_mac.mm
+++ b/components/os_crypt/common/keychain_password_mac.mm
@@ -33,6 +33,12 @@
 
 namespace {
 
+#if IS_MAS_BUILD()
+const char kAccountNameSuffix[] = " App Store Key";
+#else
+const char kAccountNameSuffix[] = " Key";
+#endif
+
 // These two strings ARE indeed user facing.  But they are used to access
 // the encryption keyword.  So as to not lose encrypted data when system
 // locale changes we DO NOT LOCALIZE.
@@ -89,16 +95,55 @@
                                   uma_result);
   };
 
-  auto password = keychain.FindGenericPassword(service_name, account_name);
+  const std::string suffixed_account_name = account_name + kAccountNameSuffix;
+  auto password =
+      keychain.FindGenericPassword(service_name, suffixed_account_name);
 
   if (password.has_value()) {
     uma_result = FindGenericPasswordResult::kPasswordFound;
     return std::string(base::as_string_view(*password));
   }
 
+  // If the suffixed account didn't exist, we should check if the legacy
+  // non-suffixed account exists. If it does we can use that key and migrate it
+  // to the new account.
+  if (password.error() == errSecItemNotFound) {
+    password = keychain.FindGenericPassword(service_name, account_name);
+
+    if (password.has_value()) {
+      // If we found the legacy account name we should copy it over to
+      // the new suffixed account
+      OSStatus error = keychain.AddGenericPassword(
+          service_name, suffixed_account_name, *password);
+
+      if (error == noErr) {
+        // If we successfully made the suffixed account we can delete the old
+        // account to ensure new apps don't try to use it and run into IAM
+        // issues
+        NSDictionary* delete_query = @{
+          (__bridge id)kSecClass : (__bridge id)kSecClassGenericPassword,
+          (__bridge id)kSecAttrService : @(service_name.c_str()),
+          (__bridge id)kSecAttrAccount : @(account_name.c_str()),
+        };
+        error = keychain.ItemDelete(
+            (__bridge CFDictionaryRef)delete_query);
+        if (error != noErr) {
+          OSSTATUS_DLOG(ERROR, error)
+              << "Keychain delete for legacy key failed";
+        }
+      } else {
+        OSSTATUS_DLOG(ERROR, error) << "Keychain add for suffixed key failed";
+      }
+
+      uma_result = FindGenericPasswordResult::kPasswordFound;
+      return std::string(base::as_string_view(*password));
+    }
+  }
+
   if (password.error() == errSecItemNotFound) {
     uma_result = FindGenericPasswordResult::kPasswordNotFound;
-    return AddRandomPasswordToKeychain(keychain, service_name, account_name);
+    return AddRandomPasswordToKeychain(keychain, service_name,
+                                       suffixed_account_name);
   }
 
   OSSTATUS_LOG(ERROR, password.error()) << "Keychain lookup failed";
