-
Notifications
You must be signed in to change notification settings - Fork 5
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
[APT-10393] Support Rotating Content URLs #46
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,6 +5,7 @@ import android.content.SharedPreferences | |
import android.util.Base64 | ||
import android.util.Log | ||
import com.scribd.armadillo.Constants | ||
import com.scribd.armadillo.error.DrmDownloadException | ||
import com.scribd.armadillo.models.DrmDownload | ||
import com.scribd.armadillo.models.DrmType | ||
import kotlinx.serialization.decodeFromString | ||
|
@@ -28,9 +29,9 @@ internal interface SecureStorage { | |
@Singleton | ||
internal class ArmadilloSecureStorage @Inject constructor( | ||
@Named(Constants.DI.STANDARD_STORAGE) private val legacyStandardStorage: SharedPreferences, | ||
@Named(Constants.DI.STANDARD_SECURE_STORAGE) private val secureStandardStorage: SharedPreferences, | ||
@Named(Constants.DI.STANDARD_SECURE_STORAGE) private val secureStandardStorage: SharedPreferences?, | ||
@Named(Constants.DI.DRM_DOWNLOAD_STORAGE) private val legacyDrmStorage: SharedPreferences, | ||
@Named(Constants.DI.DRM_SECURE_STORAGE) private val secureDrmStorage: SharedPreferences | ||
@Named(Constants.DI.DRM_SECURE_STORAGE) private val secureDrmStorage: SharedPreferences? | ||
) : SecureStorage { | ||
companion object { | ||
const val DOWNLOAD_KEY = "download_key" | ||
|
@@ -41,8 +42,8 @@ internal class ArmadilloSecureStorage @Inject constructor( | |
} | ||
|
||
override fun downloadSecretKey(context: Context): ByteArray { | ||
return if (secureStandardStorage.contains(DOWNLOAD_KEY)) { | ||
val storedKey = secureDrmStorage.getString(DOWNLOAD_KEY, DEFAULT)!! | ||
return if (secureStandardStorage?.contains(DOWNLOAD_KEY) == true) { | ||
val storedKey = secureDrmStorage?.getString(DOWNLOAD_KEY, DEFAULT)!! | ||
if (storedKey == DEFAULT) { | ||
Log.e(TAG, "Storage Is Out of Alignment") | ||
} | ||
|
@@ -53,13 +54,13 @@ internal class ArmadilloSecureStorage @Inject constructor( | |
if (storedKey == DEFAULT) { | ||
Log.e(TAG, "Storage Is Out of Alignment") | ||
} | ||
secureStandardStorage.edit().putString(DOWNLOAD_KEY, storedKey).apply() | ||
secureStandardStorage?.edit()?.putString(DOWNLOAD_KEY, storedKey)?.apply() | ||
legacyStandardStorage.edit().remove(DOWNLOAD_KEY).apply() | ||
storedKey.toSecretByteArray | ||
} else { | ||
//no key exists anywhere yet | ||
createRandomString().also { | ||
secureStandardStorage.edit().putString(DOWNLOAD_KEY, it).apply() | ||
secureStandardStorage?.edit()?.putString(DOWNLOAD_KEY, it)?.apply() | ||
}.toSecretByteArray | ||
} | ||
} | ||
|
@@ -71,48 +72,53 @@ internal class ArmadilloSecureStorage @Inject constructor( | |
} | ||
|
||
override fun saveDrmDownload(context: Context, audioUrl: String, drmDownload: DrmDownload) { | ||
if(secureDrmStorage == null){ | ||
throw DrmDownloadException(UnsupportedOperationException("This device cannot encrypt downloads")) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What should the user experience be if this happens? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Download failed. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there anything they can do to fix it? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No. If their device is in the failed mode they can't download. They can still stream even this component fails to initialize. |
||
} | ||
val alias = getDrmDownloadAlias(audioUrl, drmDownload.drmType) | ||
val value = Base64.encodeToString(Json.encodeToString(drmDownload).toByteArray(StandardCharsets.UTF_8), Base64.NO_WRAP) | ||
secureDrmStorage.edit().putString(alias, value).apply() | ||
secureDrmStorage.edit()?.putString(alias, value)?.apply() | ||
} | ||
|
||
override fun getDrmDownload(context: Context, audioUrl: String, drmType: DrmType): DrmDownload? { | ||
val alias = getDrmDownloadAlias(audioUrl, drmType) | ||
var download = secureDrmStorage.getString(alias, null)?.decodeToDrmDownload() | ||
var download = secureDrmStorage?.getString(alias, null)?.decodeToDrmDownload() | ||
if (download == null && legacyDrmStorage.contains(alias)) { | ||
//migrate old storage to secure storage | ||
val downloadValue = legacyDrmStorage.getString(alias, null) | ||
download = downloadValue?.decodeToDrmDownload() | ||
secureDrmStorage.edit().putString(alias, downloadValue).apply() | ||
legacyDrmStorage.edit().remove(alias).apply() | ||
if(secureDrmStorage != null) { | ||
secureDrmStorage.edit()?.putString(alias, downloadValue)?.apply() | ||
legacyDrmStorage.edit().remove(alias).apply() | ||
} | ||
} | ||
return download | ||
} | ||
|
||
override fun getAllDrmDownloads(context: Context): Map<String, DrmDownload> { | ||
val drmDownloads = secureDrmStorage.all.keys.mapNotNull { alias -> | ||
val drmDownloads = secureDrmStorage?.all?.keys?.mapNotNull { alias -> | ||
secureDrmStorage.getString(alias, null)?.let { drmResult -> | ||
alias to drmResult.decodeToDrmDownload() | ||
} | ||
}.toMap() | ||
}?.toMap() | ||
val legacyDownloads = legacyDrmStorage.all.keys.mapNotNull { alias -> | ||
legacyDrmStorage.getString(alias, null)?.let { drmResult -> | ||
alias to drmResult.decodeToDrmDownload() | ||
} | ||
}.toMap() | ||
|
||
return drmDownloads.plus(legacyDownloads) | ||
return legacyDownloads.plus(drmDownloads ?: emptyMap()) | ||
} | ||
|
||
override fun removeDrmDownload(context: Context, audioUrl: String, drmType: DrmType) { | ||
val alias = getDrmDownloadAlias(audioUrl, drmType) | ||
legacyDrmStorage.edit().remove(alias).apply() | ||
secureDrmStorage.edit().remove(alias).apply() | ||
secureDrmStorage?.edit()?.remove(alias)?.apply() | ||
} | ||
|
||
override fun removeDrmDownload(context: Context, key: String) { | ||
legacyDrmStorage.edit().remove(key).apply() | ||
secureDrmStorage.edit().remove(key).apply() | ||
secureDrmStorage?.edit()?.remove(key)?.apply() | ||
} | ||
|
||
private val String.toSecretByteArray: ByteArray | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What exception can actually happen here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also I think this one should maybe fall back to unencrypted storage? It seems better than not being able to do anything.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I dont know exactly, im following second hand reports on problems with EncryptedSharedPrefs.
Failing into an insecure mode would not be appropriate.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tink-crypto/tink#535 (comment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd recommend maybe doing something like this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you may be fine to use
MasterKeys.AES256_GCM_SPEC
for both instead of a custom keyspec for each too, but your call