/* _____ _
* |_ _| |_ _ _ ___ ___ _ __ __ _
* | | | ' \| '_/ -_) -_) ' \/ _` |_
* |_| |_||_|_| \___\___|_|_|_\__,_(_)
*
* Threema for Android
* Copyright (c) 2025 Threema GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ch.threema.localcrypto
import ch.threema.localcrypto.MasterKeyTestData.MASTER_KEY
import ch.threema.localcrypto.models.MasterKeyData
import ch.threema.localcrypto.models.MasterKeyState
import ch.threema.localcrypto.models.MasterKeyStorageData
import ch.threema.localcrypto.models.Version1MasterKeyStorageData
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFails
import kotlin.test.assertFalse
import kotlin.test.assertTrue
class MasterKeyStorageManagerTest {
@Test
fun `no key exists`() {
val keyStorageManager = MasterKeyStorageManager(
version2KeyFileManager = mockk {
every { keyFileExists() } returns false
},
version1KeyFileManager = mockk {
every { keyFileExists() } returns false
},
)
assertFalse(keyStorageManager.keyExists())
}
@Test
fun `reading from existing version 2 key file`() {
val keyStorageDataMock = mockk()
val keyStateMock = mockk()
val keyStorageManager = MasterKeyStorageManager(
version2KeyFileManager = mockk {
every { keyFileExists() } returns true
every { readKeyFile() } returns keyStorageDataMock
},
version1KeyFileManager = mockk(),
storageStateConverter = mockk {
every { toKeyState(keyStorageDataMock) } returns keyStateMock
},
)
assertTrue(keyStorageManager.keyExists())
assertEquals(keyStateMock, keyStorageManager.readKey())
}
@Test
fun `reading from existing version 1 key file`() {
val keyStorageDataMock = mockk()
val keyStateMock = mockk()
val keyStorageManager = MasterKeyStorageManager(
version2KeyFileManager = mockk {
every { keyFileExists() } returns false
},
version1KeyFileManager = mockk {
every { keyFileExists() } returns true
every { readKeyFile() } returns keyStorageDataMock
},
storageStateConverter = mockk {
every { toKeyState(keyStorageDataMock) } returns keyStateMock
},
)
assertTrue(keyStorageManager.keyExists())
assertEquals(keyStateMock, keyStorageManager.readKey())
}
@Test
fun `writing key when version 1 key does not exist`() {
val version2KeyFileManagerMock = mockk(relaxed = true)
val version1KeyFileManagerMock = mockk(relaxed = true) {
every { keyFileExists() } returns false
}
val keyStateMock = mockk()
val storageDataMock = mockk()
val keyStorageManager = MasterKeyStorageManager(
version2KeyFileManager = version2KeyFileManagerMock,
version1KeyFileManager = version1KeyFileManagerMock,
storageStateConverter = mockk {
every { toStorageData(keyStateMock) } returns storageDataMock
},
)
keyStorageManager.writeKey(keyStateMock)
verify(exactly = 1) { version2KeyFileManagerMock.writeKeyFile(storageDataMock) }
verify(exactly = 1) { version1KeyFileManagerMock.deleteFile() }
}
@Test
fun `writing key when version 1 key exists`() {
val version2KeyFileManagerMock = mockk(relaxed = true)
val version1KeyFileManagerMock = mockk(relaxed = true) {
every { keyFileExists() } returns true
every { readKeyFile() } returns MasterKeyStorageData.Version1(
data = Version1MasterKeyStorageData.Unprotected(
masterKeyData = MasterKeyData(
value = MASTER_KEY,
),
verification = mockk(),
),
)
}
val keyStateMock = mockk {
every { masterKeyData } returns MasterKeyData(
value = MASTER_KEY.copyOf(),
)
}
val storageDataMock = mockk()
val keyStorageManager = MasterKeyStorageManager(
version2KeyFileManager = version2KeyFileManagerMock,
version1KeyFileManager = version1KeyFileManagerMock,
storageStateConverter = mockk {
every { toStorageData(keyStateMock as MasterKeyState) } returns storageDataMock
},
)
keyStorageManager.writeKey(keyStateMock)
verify(exactly = 1) { version2KeyFileManagerMock.writeKeyFile(storageDataMock) }
verify(exactly = 1) { version1KeyFileManagerMock.deleteFile() }
}
@Test
fun `writing key fails when version 1 key exists but has different value`() {
val version2KeyFileManagerMock = mockk(relaxed = true)
val version1KeyFileManagerMock = mockk(relaxed = true) {
every { keyFileExists() } returns true
every { readKeyFile() } returns MasterKeyStorageData.Version1(
data = Version1MasterKeyStorageData.Unprotected(
masterKeyData = MasterKeyData(
value = MASTER_KEY,
),
verification = mockk(),
),
)
}
val corruptedKey = MASTER_KEY.copyOf()
corruptedKey[0]++
val keyStateMock = mockk {
every { masterKeyData } returns MasterKeyData(
value = corruptedKey,
)
}
val storageDataMock = mockk()
val keyStorageManager = MasterKeyStorageManager(
version2KeyFileManager = version2KeyFileManagerMock,
version1KeyFileManager = version1KeyFileManagerMock,
storageStateConverter = mockk {
every { toStorageData(keyStateMock as MasterKeyState) } returns storageDataMock
},
)
assertFails {
keyStorageManager.writeKey(keyStateMock)
}
verify(exactly = 0) { version2KeyFileManagerMock.writeKeyFile(storageDataMock) }
verify(exactly = 0) { version1KeyFileManagerMock.deleteFile() }
}
}