diff --git a/build.gradle.kts b/build.gradle.kts index 521b272..fe91047 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -29,7 +29,7 @@ dependencies { // Align versions of all Kotlin components implementation(platform("org.jetbrains.kotlin:kotlin-bom")) - api("tech.relaycorp:awala:[1.66.4,2.0.0)") + api("tech.relaycorp:awala:[1.68.2,2.0.0)") testImplementation("tech.relaycorp:awala-testing:1.5.26") implementation("org.mongodb:bson:4.11.1") diff --git a/src/main/kotlin/tech/relaycorp/awala/keystores/file/FileSessionPublicKeystore.kt b/src/main/kotlin/tech/relaycorp/awala/keystores/file/FileSessionPublicKeystore.kt index cf047fd..d673057 100644 --- a/src/main/kotlin/tech/relaycorp/awala/keystores/file/FileSessionPublicKeystore.kt +++ b/src/main/kotlin/tech/relaycorp/awala/keystores/file/FileSessionPublicKeystore.kt @@ -17,13 +17,17 @@ public class FileSessionPublicKeystore( @Suppress("MemberVisibilityCanBePrivate") public val rootDirectory: File = keystoreRoot.directory.resolve("public") - override suspend fun saveKeyData(keyData: SessionPublicKeyData, peerId: String) { + override suspend fun saveKeyData( + keyData: SessionPublicKeyData, + nodeId: String, + peerId: String + ) { val wasDirectoryCreated = rootDirectory.mkdirs() if (!wasDirectoryCreated && !rootDirectory.exists()) { throw FileKeystoreException("Failed to create root directory for public keys") } - val keyDataFile = getKeyDataFile(peerId) + val keyDataFile = getKeyDataFile(nodeId, peerId) val bsonSerialization = BasicOutputBuffer().use { buffer -> BsonBinaryWriter(buffer).use { it.writeStartDocument() @@ -41,8 +45,8 @@ public class FileSessionPublicKeystore( } } - override suspend fun retrieveKeyData(peerId: String): SessionPublicKeyData? { - val keyDataFile = getKeyDataFile(peerId) + override suspend fun retrieveKeyData(nodeId: String, peerId: String): SessionPublicKeyData? { + val keyDataFile = getKeyDataFile(nodeId, peerId) val serialization = try { keyDataFile.readBytes() } catch (exc: IOException) { @@ -66,11 +70,11 @@ public class FileSessionPublicKeystore( return data } - override suspend fun delete(peerId: String) { - val keyDataFile = getKeyDataFile(peerId) + override suspend fun delete(nodeId: String, peerId: String) { + val keyDataFile = getKeyDataFile(nodeId, peerId) keyDataFile.delete() } - private fun getKeyDataFile(peerId: String) = - rootDirectory.resolve(peerId) + private fun getKeyDataFile(nodeId: String, peerId: String) = + rootDirectory.resolve("$nodeId-$peerId") } diff --git a/src/test/kotlin/tech/relaycorp/awala/keystores/file/FileSessionPublicKeystoreTest.kt b/src/test/kotlin/tech/relaycorp/awala/keystores/file/FileSessionPublicKeystoreTest.kt index 64875a0..fa34837 100644 --- a/src/test/kotlin/tech/relaycorp/awala/keystores/file/FileSessionPublicKeystoreTest.kt +++ b/src/test/kotlin/tech/relaycorp/awala/keystores/file/FileSessionPublicKeystoreTest.kt @@ -30,22 +30,22 @@ import tech.relaycorp.relaynet.testing.pki.PDACertPath @ExperimentalCoroutinesApi @Suppress("BlockingMethodInNonBlockingContext") class FileSessionPublicKeystoreTest : KeystoreTestCase() { + private val nodeId = PDACertPath.PDA.issuerCommonName private val peerId = PDACertPath.PDA.subjectId private val sessionKeyPair = SessionKeyPair.generate() private val publicKeystoreRootPath = keystoreRoot.directory.resolve("public").toPath() - private val keyDataFilePath = publicKeystoreRootPath.resolve(peerId) + private val keyDataFilePath = publicKeystoreRootPath.resolve("$nodeId-$peerId") @Nested inner class Save { @Test fun `Keystore directory should be reused if it already exists`() = runTest { - @Suppress("BlockingMethodInNonBlockingContext") publicKeystoreRootPath.createDirectory() val keystore = FileSessionPublicKeystore(keystoreRoot) - keystore.save(sessionKeyPair.sessionKey, peerId) + keystore.save(sessionKeyPair.sessionKey, nodeId, peerId) readKeyData() } @@ -55,7 +55,7 @@ class FileSessionPublicKeystoreTest : KeystoreTestCase() { assertFalse(publicKeystoreRootPath.exists()) val keystore = FileSessionPublicKeystore(keystoreRoot) - keystore.save(sessionKeyPair.sessionKey, peerId) + keystore.save(sessionKeyPair.sessionKey, nodeId, peerId) readKeyData() } @@ -65,7 +65,7 @@ class FileSessionPublicKeystoreTest : KeystoreTestCase() { keystoreRoot.directory.delete() val keystore = FileSessionPublicKeystore(keystoreRoot) - keystore.save(sessionKeyPair.sessionKey, peerId) + keystore.save(sessionKeyPair.sessionKey, nodeId, peerId) readKeyData() } @@ -78,7 +78,7 @@ class FileSessionPublicKeystoreTest : KeystoreTestCase() { val keystore = FileSessionPublicKeystore(keystoreRoot) val exception = assertThrows { - keystore.save(sessionKeyPair.sessionKey, peerId) + keystore.save(sessionKeyPair.sessionKey, nodeId, peerId) } assertEquals( @@ -93,7 +93,7 @@ class FileSessionPublicKeystoreTest : KeystoreTestCase() { val creationTime = ZonedDateTime.now() val keystore = FileSessionPublicKeystore(keystoreRoot) - keystore.save(sessionKeyPair.sessionKey, peerId, creationTime) + keystore.save(sessionKeyPair.sessionKey, nodeId, peerId, creationTime) val savedKeyData = readKeyData() assertEquals( @@ -114,10 +114,10 @@ class FileSessionPublicKeystoreTest : KeystoreTestCase() { fun `Existing file should be updated if there is a prior key for peer`() = runTest { val now = ZonedDateTime.now() val keystore = FileSessionPublicKeystore(keystoreRoot) - keystore.save(sessionKeyPair.sessionKey, peerId, now.minusSeconds(1)) + keystore.save(sessionKeyPair.sessionKey, nodeId, peerId, now.minusSeconds(1)) val (newSessionKey) = SessionKeyPair.generate() - keystore.save(newSessionKey, peerId, now) + keystore.save(newSessionKey, nodeId, peerId, now) val savedKeyData = readKeyData() assertEquals( @@ -140,13 +140,14 @@ class FileSessionPublicKeystoreTest : KeystoreTestCase() { // Make the read operation work but the subsequent write operation fail keystore.save( sessionKeyPair.sessionKey, + nodeId, peerId, ZonedDateTime.now().minusDays(1) ) keyDataFilePath.toFile().setWritable(false) val exception = assertThrows { - keystore.save(sessionKeyPair.sessionKey, peerId) + keystore.save(sessionKeyPair.sessionKey, nodeId, peerId) } assertEquals( @@ -177,14 +178,14 @@ class FileSessionPublicKeystoreTest : KeystoreTestCase() { publicKeystoreRootPath.deleteExisting() val keystore = FileSessionPublicKeystore(keystoreRoot) - assertThrows { keystore.retrieve(peerId) } + assertThrows { keystore.retrieve(nodeId, peerId) } } @Test fun `Key should be reported as missing if the file doesn't exist`() = runTest { val keystore = FileSessionPublicKeystore(keystoreRoot) - assertThrows { keystore.retrieve(peerId) } + assertThrows { keystore.retrieve(nodeId, peerId) } } @Test @@ -195,7 +196,7 @@ class FileSessionPublicKeystoreTest : KeystoreTestCase() { val keystore = FileSessionPublicKeystore(keystoreRoot) val exception = assertThrows { - keystore.retrieve(peerId) + keystore.retrieve(nodeId, peerId) } assertEquals("Failed to read key file", exception.message) @@ -208,7 +209,7 @@ class FileSessionPublicKeystoreTest : KeystoreTestCase() { val keystore = FileSessionPublicKeystore(keystoreRoot) val exception = assertThrows { - keystore.retrieve(peerId) + keystore.retrieve(nodeId, peerId) } assertEquals("Key file is malformed", exception.message) @@ -224,7 +225,7 @@ class FileSessionPublicKeystoreTest : KeystoreTestCase() { val keystore = FileSessionPublicKeystore(keystoreRoot) val exception = assertThrows { - keystore.retrieve(peerId) + keystore.retrieve(nodeId, peerId) } assertEquals("Key file is malformed", exception.message) @@ -240,7 +241,7 @@ class FileSessionPublicKeystoreTest : KeystoreTestCase() { val keystore = FileSessionPublicKeystore(keystoreRoot) val exception = assertThrows { - keystore.retrieve(peerId) + keystore.retrieve(nodeId, peerId) } assertEquals("Key file is malformed", exception.message) @@ -256,7 +257,7 @@ class FileSessionPublicKeystoreTest : KeystoreTestCase() { val keystore = FileSessionPublicKeystore(keystoreRoot) val exception = assertThrows { - keystore.retrieve(peerId) + keystore.retrieve(nodeId, peerId) } assertEquals("Key file is malformed", exception.message) @@ -266,9 +267,9 @@ class FileSessionPublicKeystoreTest : KeystoreTestCase() { @Test fun `Data should be returned if file exists and is valid`() = runTest { val keystore = FileSessionPublicKeystore(keystoreRoot) - keystore.save(sessionKeyPair.sessionKey, peerId) + keystore.save(sessionKeyPair.sessionKey, nodeId, peerId) - val key = keystore.retrieve(peerId) + val key = keystore.retrieve(nodeId, peerId) assertEquals(sessionKeyPair.sessionKey, key) } @@ -300,24 +301,24 @@ class FileSessionPublicKeystoreTest : KeystoreTestCase() { publicKeystoreRootPath.deleteExisting() val keystore = FileSessionPublicKeystore(keystoreRoot) - keystore.delete(peerId) + keystore.delete(nodeId, peerId) } @Test fun `Deletion should be skipped if the file doesn't exist`() = runTest { val keystore = FileSessionPublicKeystore(keystoreRoot) - keystore.delete(peerId) + keystore.delete(nodeId, peerId) } @Test fun `File should be deleted if it exists`() = runTest { val keystore = FileSessionPublicKeystore(keystoreRoot) - keystore.save(sessionKeyPair.sessionKey, peerId) + keystore.save(sessionKeyPair.sessionKey, nodeId, peerId) - keystore.delete(peerId) + keystore.delete(nodeId, peerId) - assertThrows { keystore.retrieve(peerId) } + assertThrows { keystore.retrieve(nodeId, peerId) } } } }