Factor getting secure random bytes into Crypto interface
diff --git a/app/src/main/java/com/stevesoltys/seedvault/crypto/Crypto.kt b/app/src/main/java/com/stevesoltys/seedvault/crypto/Crypto.kt
index ebe74c0..ada5b75 100644
--- a/app/src/main/java/com/stevesoltys/seedvault/crypto/Crypto.kt
+++ b/app/src/main/java/com/stevesoltys/seedvault/crypto/Crypto.kt
@@ -13,6 +13,7 @@
import java.io.InputStream
import java.io.OutputStream
import java.security.GeneralSecurityException
+import java.security.SecureRandom
import javax.crypto.spec.SecretKeySpec
/**
@@ -34,6 +35,11 @@
internal interface Crypto {
/**
+ * Returns a ByteArray with bytes retrieved from [SecureRandom].
+ */
+ fun getRandomBytes(size: Int): ByteArray
+
+ /**
* Returns a [AesGcmHkdfStreaming] encrypting stream
* that gets encrypted and authenticated the given associated data.
*/
@@ -107,6 +113,11 @@
private val key: ByteArray by lazy {
deriveStreamKey(keyManager.getMainKey(), "app data key".toByteArray())
}
+ private val secureRandom: SecureRandom by lazy { SecureRandom() }
+
+ override fun getRandomBytes(size: Int) = ByteArray(size).apply {
+ secureRandom.nextBytes(this)
+ }
@Throws(IOException::class, GeneralSecurityException::class)
override fun newEncryptingStream(
diff --git a/app/src/main/java/com/stevesoltys/seedvault/ui/recoverycode/RecoveryCodeViewModel.kt b/app/src/main/java/com/stevesoltys/seedvault/ui/recoverycode/RecoveryCodeViewModel.kt
index ff649ee..c1bf6e3 100644
--- a/app/src/main/java/com/stevesoltys/seedvault/ui/recoverycode/RecoveryCodeViewModel.kt
+++ b/app/src/main/java/com/stevesoltys/seedvault/ui/recoverycode/RecoveryCodeViewModel.kt
@@ -22,7 +22,6 @@
import kotlinx.coroutines.launch
import org.calyxos.backup.storage.api.StorageBackup
import java.io.IOException
-import java.security.SecureRandom
internal const val WORD_NUM = 12
@@ -40,8 +39,7 @@
internal val wordList: List<CharArray> by lazy {
// we use our own entropy to not having to trust the library to use SecureRandom
- val entropy = ByteArray(Mnemonics.WordCount.COUNT_12.bitLength / 8)
- SecureRandom().nextBytes(entropy)
+ val entropy = crypto.getRandomBytes(Mnemonics.WordCount.COUNT_12.bitLength / 8)
// create the words from the entropy
Mnemonics.MnemonicCode(entropy).words
}
diff --git a/app/src/test/java/com/stevesoltys/seedvault/crypto/CryptoIntegrationTest.kt b/app/src/test/java/com/stevesoltys/seedvault/crypto/CryptoIntegrationTest.kt
index bf09d0d..7eedba1 100644
--- a/app/src/test/java/com/stevesoltys/seedvault/crypto/CryptoIntegrationTest.kt
+++ b/app/src/test/java/com/stevesoltys/seedvault/crypto/CryptoIntegrationTest.kt
@@ -2,6 +2,9 @@
import com.stevesoltys.seedvault.assertReadEquals
import com.stevesoltys.seedvault.header.HeaderReaderImpl
+import org.hamcrest.MatcherAssert.assertThat
+import org.hamcrest.Matchers.equalTo
+import org.hamcrest.Matchers.not
import org.junit.jupiter.api.Assertions.assertThrows
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
@@ -22,6 +25,14 @@
private val cleartext = Random.nextBytes(Random.nextInt(1, 422300))
@Test
+ fun `sanity check for getRandomBytes()`() {
+ assertThat(crypto.getRandomBytes(42), not(equalTo(crypto.getRandomBytes(42))))
+ assertThat(crypto.getRandomBytes(42), not(equalTo(crypto.getRandomBytes(42))))
+ assertThat(crypto.getRandomBytes(42), not(equalTo(crypto.getRandomBytes(42))))
+ assertThat(crypto.getRandomBytes(42), not(equalTo(crypto.getRandomBytes(42))))
+ }
+
+ @Test
fun `decrypting encrypted cleartext works`() {
val ad = Random.nextBytes(42)
val outputStream = ByteArrayOutputStream()