Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(FilePublicSessionKeyStore): Include nodeId in scope #117

Merged
merged 2 commits into from
Dec 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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) {
Expand All @@ -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")
}
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
Expand All @@ -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()
}
Expand All @@ -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()
}
Expand All @@ -78,7 +78,7 @@ class FileSessionPublicKeystoreTest : KeystoreTestCase() {
val keystore = FileSessionPublicKeystore(keystoreRoot)

val exception = assertThrows<FileKeystoreException> {
keystore.save(sessionKeyPair.sessionKey, peerId)
keystore.save(sessionKeyPair.sessionKey, nodeId, peerId)
}

assertEquals(
Expand All @@ -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(
Expand All @@ -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(
Expand All @@ -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<FileKeystoreException> {
keystore.save(sessionKeyPair.sessionKey, peerId)
keystore.save(sessionKeyPair.sessionKey, nodeId, peerId)
}

assertEquals(
Expand Down Expand Up @@ -177,14 +178,14 @@ class FileSessionPublicKeystoreTest : KeystoreTestCase() {
publicKeystoreRootPath.deleteExisting()
val keystore = FileSessionPublicKeystore(keystoreRoot)

assertThrows<MissingKeyException> { keystore.retrieve(peerId) }
assertThrows<MissingKeyException> { keystore.retrieve(nodeId, peerId) }
}

@Test
fun `Key should be reported as missing if the file doesn't exist`() = runTest {
val keystore = FileSessionPublicKeystore(keystoreRoot)

assertThrows<MissingKeyException> { keystore.retrieve(peerId) }
assertThrows<MissingKeyException> { keystore.retrieve(nodeId, peerId) }
}

@Test
Expand All @@ -195,7 +196,7 @@ class FileSessionPublicKeystoreTest : KeystoreTestCase() {
val keystore = FileSessionPublicKeystore(keystoreRoot)

val exception = assertThrows<FileKeystoreException> {
keystore.retrieve(peerId)
keystore.retrieve(nodeId, peerId)
}

assertEquals("Failed to read key file", exception.message)
Expand All @@ -208,7 +209,7 @@ class FileSessionPublicKeystoreTest : KeystoreTestCase() {
val keystore = FileSessionPublicKeystore(keystoreRoot)

val exception = assertThrows<FileKeystoreException> {
keystore.retrieve(peerId)
keystore.retrieve(nodeId, peerId)
}

assertEquals("Key file is malformed", exception.message)
Expand All @@ -224,7 +225,7 @@ class FileSessionPublicKeystoreTest : KeystoreTestCase() {
val keystore = FileSessionPublicKeystore(keystoreRoot)

val exception = assertThrows<FileKeystoreException> {
keystore.retrieve(peerId)
keystore.retrieve(nodeId, peerId)
}

assertEquals("Key file is malformed", exception.message)
Expand All @@ -240,7 +241,7 @@ class FileSessionPublicKeystoreTest : KeystoreTestCase() {
val keystore = FileSessionPublicKeystore(keystoreRoot)

val exception = assertThrows<FileKeystoreException> {
keystore.retrieve(peerId)
keystore.retrieve(nodeId, peerId)
}

assertEquals("Key file is malformed", exception.message)
Expand All @@ -256,7 +257,7 @@ class FileSessionPublicKeystoreTest : KeystoreTestCase() {
val keystore = FileSessionPublicKeystore(keystoreRoot)

val exception = assertThrows<FileKeystoreException> {
keystore.retrieve(peerId)
keystore.retrieve(nodeId, peerId)
}

assertEquals("Key file is malformed", exception.message)
Expand All @@ -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)
}
Expand Down Expand Up @@ -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<MissingKeyException> { keystore.retrieve(peerId) }
assertThrows<MissingKeyException> { keystore.retrieve(nodeId, peerId) }
}
}
}
Loading