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 ebe74c01..ada5b759 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.IOException import java.io.InputStream import java.io.OutputStream import java.security.GeneralSecurityException +import java.security.SecureRandom import javax.crypto.spec.SecretKeySpec /** @@ -33,6 +34,11 @@ import javax.crypto.spec.SecretKeySpec */ 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 @@ internal class CryptoImpl( 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 ff649ee5..c1bf6e33 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.GlobalScope 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 class RecoveryCodeViewModel( internal val wordList: List 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 bf09d0d2..7eedba18 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 @@ package com.stevesoltys.seedvault.crypto 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 @@ -21,6 +24,14 @@ class CryptoIntegrationTest { 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)