expo-secure-store 12.5.0 β†’ 12.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -10,6 +10,27 @@
10
10
 
11
11
  ### πŸ’‘ Others
12
12
 
13
+ ## 12.7.0 β€” 2023-11-14
14
+
15
+ ### πŸ›  Breaking changes
16
+
17
+ - Bumped iOS deployment target to 13.4. ([#25063](https://github.com/expo/expo/pull/25063) by [@gabrieldonadel](https://github.com/gabrieldonadel))
18
+ - On `Android` bump `compileSdkVersion` and `targetSdkVersion` to `34`. ([#24708](https://github.com/expo/expo/pull/24708) by [@alanjhughes](https://github.com/alanjhughes))
19
+
20
+ ### πŸ’‘ Others
21
+
22
+ - [Android] Enforce minimum authentication tag length for the `AESEncryptor` for improved security. ([#25294](https://github.com/expo/expo/pull/25294) by [@behenate](https://github.com/behenate))
23
+
24
+ ## 12.6.0 β€” 2023-10-17
25
+
26
+ ### πŸ›  Breaking changes
27
+
28
+ - Dropped support for Android SDK 21 and 22. ([#24201](https://github.com/expo/expo/pull/24201) by [@behenate](https://github.com/behenate))
29
+
30
+ ### πŸ› Bug fixes
31
+
32
+ - Fixed the 'WHEN_UNLOCKED_THIS_DEVICE_ONLY' constraint being incorrectly mapped to wrong secure store accessible ([#24831](https://github.com/expo/expo/pull/24831) by [@mmmguitar](https://github.com/mmmguitar))
33
+
13
34
  ## 12.5.0 β€” 2023-09-04
14
35
 
15
36
  ### πŸŽ‰ New features
@@ -3,15 +3,20 @@ apply plugin: 'kotlin-android'
3
3
  apply plugin: 'maven-publish'
4
4
 
5
5
  group = 'host.exp.exponent'
6
- version = '12.5.0'
6
+ version = '12.7.0'
7
7
 
8
- buildscript {
9
- def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
10
- if (expoModulesCorePlugin.exists()) {
11
- apply from: expoModulesCorePlugin
12
- applyKotlinExpoModulesCorePlugin()
8
+ def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
9
+ if (expoModulesCorePlugin.exists()) {
10
+ apply from: expoModulesCorePlugin
11
+ applyKotlinExpoModulesCorePlugin()
12
+ // Remove this check, but keep the contents after SDK49 support is dropped
13
+ if (safeExtGet("expoProvidesDefaultConfig", false)) {
14
+ useExpoPublishing()
15
+ useCoreDependencies()
13
16
  }
17
+ }
14
18
 
19
+ buildscript {
15
20
  // Simple helper that allows the root project to override versions declared by this library.
16
21
  ext.safeExtGet = { prop, fallback ->
17
22
  rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
@@ -35,23 +40,44 @@ buildscript {
35
40
  }
36
41
  }
37
42
 
38
- afterEvaluate {
39
- publishing {
40
- publications {
41
- release(MavenPublication) {
42
- from components.release
43
+ // Remove this if and it's contents, when support for SDK49 is dropped
44
+ if (!safeExtGet("expoProvidesDefaultConfig", false)) {
45
+ afterEvaluate {
46
+ publishing {
47
+ publications {
48
+ release(MavenPublication) {
49
+ from components.release
50
+ }
43
51
  }
44
- }
45
- repositories {
46
- maven {
47
- url = mavenLocal().url
52
+ repositories {
53
+ maven {
54
+ url = mavenLocal().url
55
+ }
48
56
  }
49
57
  }
50
58
  }
51
59
  }
52
60
 
53
61
  android {
54
- compileSdkVersion safeExtGet("compileSdkVersion", 33)
62
+ // Remove this if and it's contents, when support for SDK49 is dropped
63
+ if (!safeExtGet("expoProvidesDefaultConfig", false)) {
64
+ compileSdkVersion safeExtGet("compileSdkVersion", 34)
65
+
66
+ defaultConfig {
67
+ minSdkVersion safeExtGet("minSdkVersion", 23)
68
+ targetSdkVersion safeExtGet("targetSdkVersion", 34)
69
+ }
70
+
71
+ publishing {
72
+ singleVariant("release") {
73
+ withSourcesJar()
74
+ }
75
+ }
76
+
77
+ lintOptions {
78
+ abortOnError false
79
+ }
80
+ }
55
81
 
56
82
  def agpVersion = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION
57
83
  if (agpVersion.tokenize('.')[0].toInteger() < 8) {
@@ -67,25 +93,18 @@ android {
67
93
 
68
94
  namespace "expo.modules.securestore"
69
95
  defaultConfig {
70
- minSdkVersion safeExtGet("minSdkVersion", 21)
71
- targetSdkVersion safeExtGet("targetSdkVersion", 33)
72
96
  versionCode 17
73
- versionName '12.5.0'
74
- }
75
- lintOptions {
76
- abortOnError false
77
- }
78
- publishing {
79
- singleVariant("release") {
80
- withSourcesJar()
81
- }
97
+ versionName '12.7.0'
82
98
  }
83
99
  }
84
100
 
85
101
  dependencies {
86
- implementation project(':expo-modules-core')
102
+ // Remove this if and it's contents, when support for SDK49 is dropped
103
+ if (!safeExtGet("expoProvidesDefaultConfig", false)) {
104
+ implementation project(':expo-modules-core')
105
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${getKotlinVersion()}"
106
+ }
87
107
 
88
108
  api "androidx.biometric:biometric:1.1.0"
89
109
 
90
- implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${getKotlinVersion()}"
91
110
  }
@@ -2,7 +2,6 @@ package expo.modules.securestore
2
2
 
3
3
  import android.content.Context
4
4
  import android.content.SharedPreferences
5
- import android.os.Build
6
5
  import android.preference.PreferenceManager
7
6
  import android.security.keystore.KeyPermanentlyInvalidatedException
8
7
  import android.util.Log
@@ -105,7 +104,9 @@ open class SecureStoreModule : Module() {
105
104
  "but it might be raised because the keychain used to decrypt this key has been invalidated and deleted." +
106
105
  " If you are confident that the keychain you provided is correct and want to avoid this error in the " +
107
106
  "future you should save a new value under this key or use `deleteItemImpl()` and remove the existing one."
108
- } else { "" }
107
+ } else {
108
+ ""
109
+ }
109
110
 
110
111
  encryptedItemString ?: return null
111
112
 
@@ -125,24 +126,23 @@ open class SecureStoreModule : Module() {
125
126
  AESEncryptor.NAME -> {
126
127
  val secretKeyEntry = getPreferredKeyEntry(SecretKeyEntry::class.java, mAESEncryptor, options, requireAuthentication, usesKeystoreSuffix)
127
128
  ?: throw DecryptException("Could not find a keychain for key $key$legacyReadFailedWarning", key, options.keychainService)
128
- return mAESEncryptor.decryptItem(encryptedItem, secretKeyEntry, options, authenticationHelper)
129
+ return mAESEncryptor.decryptItem(key, encryptedItem, secretKeyEntry, options, authenticationHelper)
129
130
  }
130
131
  HybridAESEncryptor.NAME -> {
131
132
  val privateKeyEntry = getPreferredKeyEntry(PrivateKeyEntry::class.java, hybridAESEncryptor, options, requireAuthentication, usesKeystoreSuffix)
132
133
  ?: throw DecryptException("Could not find a keychain for key $key$legacyReadFailedWarning", key, options.keychainService)
133
- return hybridAESEncryptor.decryptItem(encryptedItem, privateKeyEntry, options, authenticationHelper)
134
+ return hybridAESEncryptor.decryptItem(key, encryptedItem, privateKeyEntry, options, authenticationHelper)
134
135
  }
135
136
  else -> {
136
137
  throw DecryptException("The item for key $key in SecureStore has an unknown encoding scheme $scheme)", key, options.keychainService)
137
138
  }
138
139
  }
140
+ } catch (e: KeyPermanentlyInvalidatedException) {
141
+ Log.w(TAG, "The requested key has been permanently invalidated. Returning null")
142
+ return null
143
+ } catch (e: BadPaddingException) {
144
+ throw (DecryptException("Could not decrypt the value with provided keychain $legacyReadFailedWarning", key, options.keychainService, e))
139
145
  } catch (e: GeneralSecurityException) {
140
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && e is KeyPermanentlyInvalidatedException) {
141
- Log.w(TAG, "The requested key has been permanently invalidated. Returning null")
142
- return null
143
- } else if (e is BadPaddingException) {
144
- throw (DecryptException("Could not decrypt the value with provided keychain $legacyReadFailedWarning", key, options.keychainService, e))
145
- }
146
146
  throw (DecryptException(e.message, key, options.keychainService, e))
147
147
  } catch (e: CodedException) {
148
148
  throw e
@@ -176,35 +176,23 @@ open class SecureStoreModule : Module() {
176
176
  use in the encrypted JSON item so that we know how to decode and decrypt it when reading
177
177
  back a value.
178
178
  */
179
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
180
- val secretKeyEntry: SecretKeyEntry = getKeyEntry(SecretKeyEntry::class.java, mAESEncryptor, options, options.requireAuthentication)
181
- val encryptedItem = mAESEncryptor.createEncryptedItem(value, secretKeyEntry, options.requireAuthentication, options.authenticationPrompt, authenticationHelper)
182
- encryptedItem.put(SCHEME_PROPERTY, AESEncryptor.NAME)
183
- saveEncryptedItem(encryptedItem, prefs, keychainAwareKey, options.requireAuthentication, options.keychainService)
184
- } else {
185
- val privateKeyEntry: PrivateKeyEntry = getKeyEntry(PrivateKeyEntry::class.java, hybridAESEncryptor, options, options.requireAuthentication)
186
- val encryptedItem = hybridAESEncryptor.createEncryptedItem(value, privateKeyEntry, options.requireAuthentication, options.authenticationPrompt, authenticationHelper)
187
- encryptedItem.put(SCHEME_PROPERTY, HybridAESEncryptor.NAME)
188
- saveEncryptedItem(encryptedItem, prefs, keychainAwareKey, options.requireAuthentication, options.keychainService)
189
- }
179
+ val secretKeyEntry: SecretKeyEntry = getKeyEntry(SecretKeyEntry::class.java, mAESEncryptor, options, options.requireAuthentication)
180
+ val encryptedItem = mAESEncryptor.createEncryptedItem(value, secretKeyEntry, options.requireAuthentication, options.authenticationPrompt, authenticationHelper)
181
+ encryptedItem.put(SCHEME_PROPERTY, AESEncryptor.NAME)
182
+ saveEncryptedItem(encryptedItem, prefs, keychainAwareKey, options.requireAuthentication, options.keychainService)
190
183
 
191
184
  // If a legacy value exists under this key we remove it to avoid unexpected errors in the future
192
185
  if (prefs.contains(key)) {
193
186
  prefs.edit().remove(key).apply()
194
187
  }
195
- } catch (e: GeneralSecurityException) {
196
- val isInvalidationException = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && e is KeyPermanentlyInvalidatedException
197
-
198
- if (isInvalidationException && !keyIsInvalidated) {
199
- // If the key has been invalidated by the OS we try to reinitialize it in the save retry.
188
+ } catch (e: KeyPermanentlyInvalidatedException) {
189
+ if (!keyIsInvalidated) {
200
190
  Log.w(TAG, "Key has been invalidated, retrying with the key deleted")
201
- setItemImpl(key, value, options, true)
202
- } else if (isInvalidationException) {
203
- // If reinitialization of the key fails, reject the promise
204
- throw EncryptException("Encryption Failed. The key $key has been permanently invalidated and cannot be reinitialized", key, options.keychainService, e)
205
- } else {
206
- throw EncryptException(e.message, key, options.keychainService, e)
191
+ return setItemImpl(key, value, options, true)
207
192
  }
193
+ throw EncryptException("Encryption Failed. The key $key has been permanently invalidated and cannot be reinitialized", key, options.keychainService, e)
194
+ } catch (e: GeneralSecurityException) {
195
+ throw EncryptException(e.message, key, options.keychainService, e)
208
196
  } catch (e: CodedException) {
209
197
  throw e
210
198
  } catch (e: Exception) {
@@ -5,6 +5,7 @@ import android.security.keystore.KeyGenParameterSpec
5
5
  import android.security.keystore.KeyProperties
6
6
  import android.util.Base64
7
7
  import expo.modules.securestore.AuthenticationHelper
8
+ import expo.modules.securestore.DecryptException
8
9
  import expo.modules.securestore.SecureStoreModule
9
10
  import expo.modules.securestore.SecureStoreOptions
10
11
  import org.json.JSONException
@@ -108,6 +109,7 @@ class AESEncryptor : KeyBasedEncryptor<KeyStore.SecretKeyEntry> {
108
109
 
109
110
  @Throws(GeneralSecurityException::class, JSONException::class)
110
111
  override suspend fun decryptItem(
112
+ key: String,
111
113
  encryptedItem: JSONObject,
112
114
  keyStoreEntry: KeyStore.SecretKeyEntry,
113
115
  options: SecureStoreOptions,
@@ -122,6 +124,9 @@ class AESEncryptor : KeyBasedEncryptor<KeyStore.SecretKeyEntry> {
122
124
  val cipher = Cipher.getInstance(AES_CIPHER)
123
125
  val requiresAuthentication = encryptedItem.optBoolean(AuthenticationHelper.REQUIRE_AUTHENTICATION_PROPERTY)
124
126
 
127
+ if (authenticationTagLength < MIN_GCM_AUTHENTICATION_TAG_LENGTH) {
128
+ throw DecryptException("Authentication tag length must be at least $MIN_GCM_AUTHENTICATION_TAG_LENGTH bits long", key, options.keychainService)
129
+ }
125
130
  cipher.init(Cipher.DECRYPT_MODE, keyStoreEntry.secretKey, gcmSpec)
126
131
  val unlockedCipher = authenticationHelper.authenticateCipher(cipher, requiresAuthentication, options.authenticationPrompt)
127
132
  return String(unlockedCipher.doFinal(ciphertextBytes), StandardCharsets.UTF_8)
@@ -134,5 +139,6 @@ class AESEncryptor : KeyBasedEncryptor<KeyStore.SecretKeyEntry> {
134
139
  private const val CIPHERTEXT_PROPERTY = "ct"
135
140
  const val IV_PROPERTY = "iv"
136
141
  private const val GCM_AUTHENTICATION_TAG_LENGTH_PROPERTY = "tlen"
142
+ private const val MIN_GCM_AUTHENTICATION_TAG_LENGTH = 96
137
143
  }
138
144
  }
@@ -2,35 +2,25 @@ package expo.modules.securestore.encryptors
2
2
 
3
3
  import android.annotation.SuppressLint
4
4
  import android.content.Context
5
- import android.os.Build
6
- import android.security.KeyPairGeneratorSpec
7
5
  import android.security.keystore.KeyProperties
8
6
  import android.util.Base64
9
- import android.util.Log
10
7
  import expo.modules.securestore.AuthenticationHelper
8
+ import expo.modules.securestore.EncryptException
9
+ import expo.modules.securestore.KeyStoreException
11
10
  import expo.modules.securestore.SecureStoreModule
12
11
  import expo.modules.securestore.SecureStoreOptions
13
12
  import org.json.JSONException
14
13
  import org.json.JSONObject
15
- import java.math.BigInteger
16
14
  import java.security.GeneralSecurityException
17
- import java.security.InvalidAlgorithmParameterException
18
- import java.security.KeyPairGenerator
19
15
  import java.security.KeyStore
20
16
  import java.security.NoSuchAlgorithmException
21
17
  import java.security.NoSuchProviderException
22
18
  import java.security.SecureRandom
23
- import java.security.UnrecoverableEntryException
24
- import java.security.spec.AlgorithmParameterSpec
25
- import java.security.spec.InvalidParameterSpecException
26
19
  import java.util.*
27
20
  import javax.crypto.Cipher
28
- import javax.crypto.KeyGenerator
29
21
  import javax.crypto.NoSuchPaddingException
30
22
  import javax.crypto.SecretKey
31
- import javax.crypto.spec.GCMParameterSpec
32
23
  import javax.crypto.spec.SecretKeySpec
33
- import javax.security.auth.x500.X500Principal
34
24
 
35
25
  /**
36
26
  * An AES encryptor that works with Android L (API 22) and below, which cannot store symmetric
@@ -66,26 +56,11 @@ class HybridAESEncryptor(private var mContext: Context, private val mAESEncrypto
66
56
 
67
57
  @Throws(GeneralSecurityException::class)
68
58
  override fun initializeKeyStoreEntry(keyStore: KeyStore, options: SecureStoreOptions): KeyStore.PrivateKeyEntry {
69
- val keystoreAlias = getExtendedKeyStoreAlias(options, options.requireAuthentication)
70
- // See https://tools.ietf.org/html/rfc1779#section-2.3 for the DN grammar
71
- val escapedCommonName = '"'.toString() + keystoreAlias.replace("\\", "\\\\").replace("\"", "\\\"") + '"'
72
-
73
- @Suppress("DEPRECATION") // This will only be called on API level < 23
74
- val algorithmSpec: AlgorithmParameterSpec = KeyPairGeneratorSpec.Builder(mContext)
75
- .setAlias(keystoreAlias)
76
- .setSubject(X500Principal("CN=$escapedCommonName, OU=SecureStore"))
77
- .setSerialNumber(BigInteger(X509_SERIAL_NUMBER_LENGTH_BITS, mSecureRandom))
78
- .setStartDate(Date(0))
79
- .setEndDate(Date(Long.MAX_VALUE))
80
- .build()
81
-
82
- @SuppressLint("InlinedApi") // constant value will be copied
83
- val keyPairGenerator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, keyStore.provider)
84
-
85
- keyPairGenerator.initialize(algorithmSpec)
86
- keyPairGenerator.generateKeyPair()
87
- return keyStore.getEntry(keystoreAlias, null) as? KeyStore.PrivateKeyEntry
88
- ?: throw UnrecoverableEntryException("Could not retrieve the newly generated private key entry")
59
+ // This should never be called after we dropped Android SDK 22 support.
60
+ throw KeyStoreException(
61
+ "Tried to initialize HybridAESEncryptor key store entry on Android SDK >= 23. This shouldn't happen. " +
62
+ "If you see this message report an issue at https://github.com/expo/expo."
63
+ )
89
64
  }
90
65
 
91
66
  @Throws(GeneralSecurityException::class, JSONException::class)
@@ -96,60 +71,22 @@ class HybridAESEncryptor(private var mContext: Context, private val mAESEncrypto
96
71
  authenticationPrompt: String,
97
72
  authenticationHelper: AuthenticationHelper
98
73
  ): JSONObject {
99
- // Generate the IV and symmetric key with which we encrypt the value
100
- val ivBytes = ByteArray(GCM_IV_LENGTH_BYTES)
101
- mSecureRandom.nextBytes(ivBytes)
102
-
103
- // constant value will be copied
104
- @SuppressLint("InlinedApi") val keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES)
105
- keyGenerator.init(AESEncryptor.AES_KEY_SIZE_BITS)
106
- val secretKey = keyGenerator.generateKey()
107
-
108
- // Encrypt the value with the symmetric key. We need to specify the GCM parameters since the
109
- // our secret key isn't tied to the keystore and the cipher can't use the secret key to
110
- // generate the parameters.
111
- val gcmSpec = GCMParameterSpec(GCM_AUTHENTICATION_TAG_LENGTH_BITS, ivBytes)
112
- val aesCipher = Cipher.getInstance(AESEncryptor.AES_CIPHER)
113
-
114
- aesCipher.init(Cipher.ENCRYPT_MODE, secretKey, gcmSpec)
115
- val chosenSpec = try {
116
- aesCipher.parameters.getParameterSpec(GCMParameterSpec::class.java)
117
- } catch (e: InvalidParameterSpecException) {
118
- // BouncyCastle tried to instantiate GCMParameterSpec using invalid constructor
119
- // https://github.com/bcgit/bc-java/commit/507c3917c0c469d10b9f033ad641c1da195e2039#diff-c90a59e823805b6c0dcfeaf7bae65f53
120
-
121
- // Let's do some sanity checks and use the spec we've initialized the cipher with.
122
- if ("GCM" != aesCipher.parameters.algorithm) {
123
- throw InvalidAlgorithmParameterException("Algorithm chosen by the cipher (" + aesCipher.parameters.algorithm + ") doesn't match requested (GCM).")
124
- }
125
- gcmSpec
126
- }
127
-
128
- val authenticatedCipher = authenticationHelper.authenticateCipher(aesCipher, requireAuthentication, authenticationPrompt)
129
-
130
- val aesResult = mAESEncryptor.createEncryptedItemWithCipher(plaintextValue, authenticatedCipher, chosenSpec)
131
-
132
- // Ensure the IV in the encrypted item matches our generated IV
133
- val ivString = aesResult.getString(AESEncryptor.IV_PROPERTY)
134
- val expectedIVString = Base64.encodeToString(ivBytes, Base64.NO_WRAP)
135
- if (ivString != expectedIVString) {
136
- Log.e(SecureStoreModule.TAG, String.format("HybridAESEncrypter generated two different IVs: %s and %s", expectedIVString, ivString))
137
- throw IllegalStateException("HybridAESEncryptor must store the same IV as the one used to parameterize the secret key")
138
- }
139
-
140
- // Encrypt the symmetric key with the asymmetric public key
141
- val secretKeyBytes = secretKey.encoded
142
- val cipher: Cipher = rSACipher
143
- cipher.init(Cipher.ENCRYPT_MODE, keyStoreEntry.certificate)
144
- val encryptedSecretKeyBytes = cipher.doFinal(secretKeyBytes)
145
- val encryptedSecretKeyString = Base64.encodeToString(encryptedSecretKeyBytes, Base64.NO_WRAP)
146
- aesResult.put(ENCRYPTED_SECRET_KEY_PROPERTY, encryptedSecretKeyString)
147
-
148
- return aesResult
74
+ // This should never be called after we dropped Android SDK 22 support.
75
+ throw EncryptException(
76
+ "HybridAESEncryption should not be used on Android SDK >= 23. This shouldn't happen. " +
77
+ "If you see this message report an issue at https://github.com/expo/expo.",
78
+ "unknown", "unknown"
79
+ )
149
80
  }
150
81
 
151
82
  @Throws(GeneralSecurityException::class, JSONException::class)
152
- override suspend fun decryptItem(encryptedItem: JSONObject, keyStoreEntry: KeyStore.PrivateKeyEntry, options: SecureStoreOptions, authenticationHelper: AuthenticationHelper): String {
83
+ override suspend fun decryptItem(
84
+ key: String,
85
+ encryptedItem: JSONObject,
86
+ keyStoreEntry: KeyStore.PrivateKeyEntry,
87
+ options: SecureStoreOptions,
88
+ authenticationHelper: AuthenticationHelper
89
+ ): String {
153
90
  // Decrypt the encrypted symmetric key
154
91
  val encryptedSecretKeyString = encryptedItem.getString(ENCRYPTED_SECRET_KEY_PROPERTY)
155
92
  val encryptedSecretKeyBytes = Base64.decode(encryptedSecretKeyString, Base64.DEFAULT)
@@ -161,20 +98,16 @@ class HybridAESEncryptor(private var mContext: Context, private val mAESEncrypto
161
98
 
162
99
  // Decrypt the value with the symmetric key
163
100
  val secretKeyEntry = KeyStore.SecretKeyEntry(secretKey)
164
- return mAESEncryptor.decryptItem(encryptedItem, secretKeyEntry, options, authenticationHelper)
101
+ return mAESEncryptor.decryptItem(key, encryptedItem, secretKeyEntry, options, authenticationHelper)
165
102
  }
166
103
 
167
104
  @get:Throws(NoSuchAlgorithmException::class, NoSuchProviderException::class, NoSuchPaddingException::class)
168
105
  private val rSACipher: Cipher
169
- get() = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) Cipher.getInstance(RSA_CIPHER, RSA_CIPHER_LEGACY_PROVIDER) else Cipher.getInstance(RSA_CIPHER)
106
+ get() = Cipher.getInstance(RSA_CIPHER)
170
107
 
171
108
  companion object {
172
109
  const val NAME = "hybrid"
173
110
  private const val RSA_CIPHER = "RSA/None/PKCS1Padding"
174
- private const val RSA_CIPHER_LEGACY_PROVIDER = "AndroidOpenSSL"
175
- private const val X509_SERIAL_NUMBER_LENGTH_BITS = 20 * 8
176
- private const val GCM_IV_LENGTH_BYTES = 12
177
- private const val GCM_AUTHENTICATION_TAG_LENGTH_BITS = 128
178
111
  private const val ENCRYPTED_SECRET_KEY_PROPERTY = "esk"
179
112
  }
180
113
  }
@@ -30,6 +30,7 @@ interface KeyBasedEncryptor<E : KeyStore.Entry> {
30
30
 
31
31
  @Throws(GeneralSecurityException::class, JSONException::class)
32
32
  suspend fun decryptItem(
33
+ key: String,
33
34
  encryptedItem: JSONObject,
34
35
  keyStoreEntry: E,
35
36
  options: SecureStoreOptions,
@@ -10,7 +10,7 @@ Pod::Spec.new do |s|
10
10
  s.license = package['license']
11
11
  s.author = package['author']
12
12
  s.homepage = package['homepage']
13
- s.platform = :ios, '13.0'
13
+ s.platform = :ios, '13.4'
14
14
  s.swift_version = '5.4'
15
15
  s.source = { git: 'https://github.com/expo/expo.git' }
16
16
  s.static_framework = true
@@ -22,7 +22,7 @@ Pod::Spec.new do |s|
22
22
  'DEFINES_MODULE' => 'YES',
23
23
  'SWIFT_COMPILATION_MODE' => 'wholemodule'
24
24
  }
25
-
25
+
26
26
  if !$ExpoUseSources&.include?(package['name']) && ENV['EXPO_USE_SOURCE'].to_i == 0 && File.exist?("#{s.name}.xcframework") && Gem::Version.new(Pod::VERSION) >= Gem::Version.new('1.10.0')
27
27
  s.source_files = "**/*.h"
28
28
  s.vendored_frameworks = "#{s.name}.xcframework"
@@ -13,7 +13,7 @@ public final class SecureStoreModule: Module {
13
13
  "WHEN_PASSCODE_SET_THIS_DEVICE_ONLY": SecureStoreAccessible.whenPasscodeSetThisDeviceOnly.rawValue,
14
14
  "ALWAYS_THIS_DEVICE_ONLY": SecureStoreAccessible.alwaysThisDeviceOnly.rawValue,
15
15
  "WHEN_UNLOCKED": SecureStoreAccessible.whenUnlocked.rawValue,
16
- "WHEN_UNLOCKED_THIS_DEVICE_ONLY": SecureStoreAccessible.whenPasscodeSetThisDeviceOnly.rawValue
16
+ "WHEN_UNLOCKED_THIS_DEVICE_ONLY": SecureStoreAccessible.whenUnlockedThisDeviceOnly.rawValue
17
17
  ])
18
18
 
19
19
  AsyncFunction("getValueWithKeyAsync") { (key: String, options: SecureStoreOptions) -> String? in
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-secure-store",
3
- "version": "12.5.0",
3
+ "version": "12.7.0",
4
4
  "description": "Provides a way to encrypt and securely store key–value pairs locally on the device.",
5
5
  "main": "build/SecureStore.js",
6
6
  "types": "build/SecureStore.d.ts",
@@ -42,5 +42,5 @@
42
42
  "peerDependencies": {
43
43
  "expo": "*"
44
44
  },
45
- "gitHead": "79607a7325f47aa17c36d266100d09a4ff2cc544"
45
+ "gitHead": "3142a086578deffd8704a8f1b6f0f661527d836c"
46
46
  }