Factor getting secure random bytes into Crypto interface

This commit is contained in:
Torsten Grote 2021-09-16 15:14:51 +02:00 committed by Chirayu Desai
parent a77d927624
commit 39cb0c6443
3 changed files with 23 additions and 3 deletions

View file

@ -13,6 +13,7 @@ import java.io.IOException
import java.io.InputStream import java.io.InputStream
import java.io.OutputStream import java.io.OutputStream
import java.security.GeneralSecurityException import java.security.GeneralSecurityException
import java.security.SecureRandom
import javax.crypto.spec.SecretKeySpec import javax.crypto.spec.SecretKeySpec
/** /**
@ -33,6 +34,11 @@ import javax.crypto.spec.SecretKeySpec
*/ */
internal interface Crypto { internal interface Crypto {
/**
* Returns a ByteArray with bytes retrieved from [SecureRandom].
*/
fun getRandomBytes(size: Int): ByteArray
/** /**
* Returns a [AesGcmHkdfStreaming] encrypting stream * Returns a [AesGcmHkdfStreaming] encrypting stream
* that gets encrypted and authenticated the given associated data. * that gets encrypted and authenticated the given associated data.
@ -107,6 +113,11 @@ internal class CryptoImpl(
private val key: ByteArray by lazy { private val key: ByteArray by lazy {
deriveStreamKey(keyManager.getMainKey(), "app data key".toByteArray()) 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) @Throws(IOException::class, GeneralSecurityException::class)
override fun newEncryptingStream( override fun newEncryptingStream(

View file

@ -22,7 +22,6 @@ import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.calyxos.backup.storage.api.StorageBackup import org.calyxos.backup.storage.api.StorageBackup
import java.io.IOException import java.io.IOException
import java.security.SecureRandom
internal const val WORD_NUM = 12 internal const val WORD_NUM = 12
@ -40,8 +39,7 @@ internal class RecoveryCodeViewModel(
internal val wordList: List<CharArray> by lazy { internal val wordList: List<CharArray> by lazy {
// we use our own entropy to not having to trust the library to use SecureRandom // we use our own entropy to not having to trust the library to use SecureRandom
val entropy = ByteArray(Mnemonics.WordCount.COUNT_12.bitLength / 8) val entropy = crypto.getRandomBytes(Mnemonics.WordCount.COUNT_12.bitLength / 8)
SecureRandom().nextBytes(entropy)
// create the words from the entropy // create the words from the entropy
Mnemonics.MnemonicCode(entropy).words Mnemonics.MnemonicCode(entropy).words
} }

View file

@ -2,6 +2,9 @@ package com.stevesoltys.seedvault.crypto
import com.stevesoltys.seedvault.assertReadEquals import com.stevesoltys.seedvault.assertReadEquals
import com.stevesoltys.seedvault.header.HeaderReaderImpl 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.Assertions.assertThrows
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance import org.junit.jupiter.api.TestInstance
@ -21,6 +24,14 @@ class CryptoIntegrationTest {
private val cleartext = Random.nextBytes(Random.nextInt(1, 422300)) 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 @Test
fun `decrypting encrypted cleartext works`() { fun `decrypting encrypted cleartext works`() {
val ad = Random.nextBytes(42) val ad = Random.nextBytes(42)