GH-380 Use Internal Preferences for Secure Storage consistency. (#386)

* Use Internal Preferences for Secure Storage consistency.
Save if we created key pre-M so we always use pre-M if device upgrades.

* Fix logic for pre-m key check

The logic was slightly off, I think this fixes it, but would be good to have another set of eyes...

1. We check to see if the device is pre-M (if it does _not_ have `M`, or if we already set the fact it's pre-M in the preference - aka from a previous install before an upgrade of the OS)
2. If we aren't pre-M, we can't use Symmetric Key from Keystore
3. If we make it down to using Asymmetric Key, we set the pre-M preference to `true` to persist the value for future invocations, which will make it 'stick' in the event of a pre-M to M+ OS upgrade.

* Address feedback on key naming.

* Added test for secure storage to simulate upgrade

From API < 23 to API >= 23 after storing data with an asymmetric key and then moving to a platform supporting symmetric keys.

* Ensure we always set flags when using specified keygen
This commit is contained in:
James Montemagno 2018-07-20 12:15:20 -07:00 коммит произвёл GitHub
Родитель 64718b8917
Коммит 4da4ea30ae
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
2 изменённых файлов: 61 добавлений и 59 удалений

Просмотреть файл

@ -85,5 +85,30 @@ namespace DeviceTests
foreach (var key in keys)
Assert.Null(await SecureStorage.GetAsync(key));
}
#if __ANDROID__
[Fact]
public async Task Asymmetric_to_Symmetric_API_Upgrade()
{
var key = "asym_to_sym_upgrade";
var expected = "this is the value";
SecureStorage.RemoveAll();
// Emulate pre api 23
SecureStorage.AlwaysUseAsymmetricKeyStorage = true;
await SecureStorage.SetAsync(key, expected);
// Simulate Upgrading to API23+
SecureStorage.AlwaysUseAsymmetricKeyStorage = false;
var v = await SecureStorage.GetAsync(key);
SecureStorage.RemoveAll();
Assert.Equal(expected, v);
}
#endif
}
}

Просмотреть файл

@ -18,9 +18,8 @@ namespace Xamarin.Essentials
{
var context = Platform.AppContext;
string encStr;
using (var prefs = context.GetSharedPreferences(Alias, FileCreationMode.Private))
encStr = prefs.GetString(Utils.Md5Hash(key), null);
string defaultEncStr = null;
var encStr = Preferences.Get(Utils.Md5Hash(key), defaultEncStr, Alias);
string decryptedData = null;
if (!string.IsNullOrEmpty(encStr))
@ -40,13 +39,8 @@ namespace Xamarin.Essentials
var ks = new AndroidKeyStore(context, Alias, AlwaysUseAsymmetricKeyStorage);
var encryptedData = ks.Encrypt(data);
using (var prefs = context.GetSharedPreferences(Alias, FileCreationMode.Private))
using (var prefsEditor = prefs.Edit())
{
var encStr = Convert.ToBase64String(encryptedData);
prefsEditor.PutString(Utils.Md5Hash(key), encStr);
prefsEditor.Commit();
}
var encStr = Convert.ToBase64String(encryptedData);
Preferences.Set(Utils.Md5Hash(key), encStr, Alias);
return Task.CompletedTask;
}
@ -56,36 +50,13 @@ namespace Xamarin.Essentials
var context = Platform.AppContext;
key = Utils.Md5Hash(key);
using (var prefs = context.GetSharedPreferences(Alias, FileCreationMode.Private))
{
if (prefs.Contains(key))
{
using (var prefsEditor = prefs.Edit())
{
prefsEditor.Remove(key);
prefsEditor.Commit();
return true;
}
}
}
Preferences.Remove(key, Alias);
return false;
}
static void PlatformRemoveAll()
{
var context = Platform.AppContext;
using (var prefs = context.GetSharedPreferences(Alias, FileCreationMode.Private))
using (var prefsEditor = prefs.Edit())
{
foreach (var key in prefs.All.Keys)
prefsEditor.Remove(key);
prefsEditor.Commit();
}
}
static void PlatformRemoveAll() =>
Preferences.Clear(Alias);
internal static bool AlwaysUseAsymmetricKeyStorage { get; set; } = false;
}
@ -114,10 +85,18 @@ namespace Xamarin.Essentials
KeyStore keyStore;
bool alwaysUseAsymmetricKey;
bool useSymmetric = false;
string useSymmetricPreferenceKey = "essentials_use_symmetric";
ISecretKey GetKey()
{
// check to see if we need to get our key from past-versions or newer versions.
// we want to use symmetric if we are >= 23 or we didn't set it previously.
useSymmetric = Preferences.Get(useSymmetricPreferenceKey, Platform.HasApiLevel(BuildVersionCodes.M), SecureStorage.Alias);
// If >= API 23 we can use the KeyStore's symmetric key
if (Platform.HasApiLevel(BuildVersionCodes.M) && !alwaysUseAsymmetricKey)
if (useSymmetric && !alwaysUseAsymmetricKey)
return GetSymmetricKey();
// NOTE: KeyStore in < API 23 can only store asymmetric keys
@ -131,40 +110,35 @@ namespace Xamarin.Essentials
// Get the asymmetric key pair
var keyPair = GetAsymmetricKeyPair();
using (var prefs = appContext.GetSharedPreferences(alias, FileCreationMode.Private))
var existingKeyStr = Preferences.Get(prefsMasterKey, null, alias);
if (!string.IsNullOrEmpty(existingKeyStr))
{
var existingKeyStr = prefs.GetString(prefsMasterKey, null);
var wrappedKey = Convert.FromBase64String(existingKeyStr);
if (!string.IsNullOrEmpty(existingKeyStr))
{
var wrappedKey = Convert.FromBase64String(existingKeyStr);
var unwrappedKey = UnwrapKey(wrappedKey, keyPair.Private);
var kp = unwrappedKey.JavaCast<ISecretKey>();
var unwrappedKey = UnwrapKey(wrappedKey, keyPair.Private);
var kp = unwrappedKey.JavaCast<ISecretKey>();
return kp;
}
else
{
var keyGenerator = KeyGenerator.GetInstance(aesAlgorithm);
var defSymmetricKey = keyGenerator.GenerateKey();
return kp;
}
else
{
var keyGenerator = KeyGenerator.GetInstance(aesAlgorithm);
var defSymmetricKey = keyGenerator.GenerateKey();
var wrappedKey = WrapKey(defSymmetricKey, keyPair.Public);
var wrappedKey = WrapKey(defSymmetricKey, keyPair.Public);
Preferences.Set(prefsMasterKey, Convert.ToBase64String(wrappedKey), alias);
using (var prefsEditor = prefs.Edit())
{
prefsEditor.PutString(prefsMasterKey, Convert.ToBase64String(wrappedKey));
prefsEditor.Commit();
}
return defSymmetricKey;
}
return defSymmetricKey;
}
}
// API 23+ Only
ISecretKey GetSymmetricKey()
{
Preferences.Set(useSymmetricPreferenceKey, true, SecureStorage.Alias);
var existingKey = keyStore.GetKey(alias, null);
if (existingKey != null)
@ -186,6 +160,9 @@ namespace Xamarin.Essentials
KeyPair GetAsymmetricKeyPair()
{
// set that we generated keys on pre-m device.
Preferences.Set(useSymmetricPreferenceKey, false, SecureStorage.Alias);
var asymmetricAlias = $"{alias}.asymmetric";
var privateKey = keyStore.GetKey(asymmetricAlias, null)?.JavaCast<IPrivateKey>();