From 43b6754b721b8f469c5c55ecabb9fb836d31272f Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 25 Jul 2024 12:51:23 +0000
Subject: [PATCH] chore(deps): bump org.jlleitschuh.gradle.ktlint from 11.6.1
to 12.1.1 (#136)
Bumps org.jlleitschuh.gradle.ktlint from 11.6.1 to 12.1.1.
[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=org.jlleitschuh.gradle.ktlint&package-manager=gradle&previous-version=11.6.1&new-version=12.1.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
You can trigger a rebase of this PR by commenting `@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
> **Note**
> Automatic rebases have been disabled on this pull request as it has been open for over 30 days.
---
build.gradle.kts | 19 +-
.../keystores/file/FileCertificateStore.kt | 49 +-
.../keystores/file/FileKeystoreException.kt | 6 +-
.../awala/keystores/file/FileKeystoreRoot.kt | 32 +-
.../keystores/file/FilePrivateKeyStore.kt | 42 +-
.../file/FileSessionPublicKeystore.kt | 77 +-
.../file/FileCertificateStoreTest.kt | 545 +++++++-------
.../keystores/file/FileKeystoreRootTest.kt | 30 +-
.../keystores/file/FilePrivateKeyStoreTest.kt | 688 +++++++++---------
.../file/FileSessionPublicKeystoreTest.kt | 392 +++++-----
.../keystores/file/MockFilePrivateKeyStore.kt | 6 +-
.../file/PrivateKeyStoreRetrievalTestCase.kt | 47 +-
.../file/PrivateKeyStoreSavingTestCase.kt | 108 +--
13 files changed, 1098 insertions(+), 943 deletions(-)
diff --git a/build.gradle.kts b/build.gradle.kts
index dbd4976..22c3980 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -10,7 +10,7 @@ plugins {
// Apply the java-library plugin for API and implementation separation.
`java-library`
- id("org.jlleitschuh.gradle.ktlint") version "11.6.1"
+ id("org.jlleitschuh.gradle.ktlint") version "12.1.1"
jacoco
@@ -104,9 +104,10 @@ tasks.withType().configureEach {
kotlinOptions {
jvmTarget = "1.8"
allWarningsAsErrors = true
- freeCompilerArgs = freeCompilerArgs + arrayOf(
- "-opt-in=kotlin.RequiresOptIn"
- )
+ freeCompilerArgs = freeCompilerArgs +
+ arrayOf(
+ "-opt-in=kotlin.RequiresOptIn",
+ )
}
}
@@ -129,7 +130,7 @@ publishing {
pom {
name.set(rootProject.name)
description.set(
- "JVM implementation of file-based Private and Public Key Stores for Awala"
+ "JVM implementation of file-based Private and Public Key Stores for Awala",
)
url.set("https://github.com/relaycorp/awala-keystore-file-jvm")
developers {
@@ -146,10 +147,10 @@ publishing {
}
scm {
connection.set(
- "scm:git:https://github.com/relaycorp/awala-keystore-file-jvm.git"
+ "scm:git:https://github.com/relaycorp/awala-keystore-file-jvm.git",
)
developerConnection.set(
- "scm:git:https://github.com/relaycorp/awala-keystore-file-jvm.git"
+ "scm:git:https://github.com/relaycorp/awala-keystore-file-jvm.git",
)
url.set("https://github.com/relaycorp/awala-keystore-file-jvm")
}
@@ -171,7 +172,7 @@ nexusPublishing {
sonatype {
nexusUrl.set(uri("https://s01.oss.sonatype.org/service/local/"))
snapshotRepositoryUrl.set(
- uri("https://s01.oss.sonatype.org/content/repositories/snapshots/")
+ uri("https://s01.oss.sonatype.org/content/repositories/snapshots/"),
)
username.set(System.getenv("MAVEN_USERNAME"))
password.set(System.getenv("MAVEN_PASSWORD"))
@@ -183,7 +184,7 @@ tasks.publish {
}
configure {
- version.set("0.42.1")
+ version.set("1.3.1")
}
gradleEnterprise {
diff --git a/src/main/kotlin/tech/relaycorp/awala/keystores/file/FileCertificateStore.kt b/src/main/kotlin/tech/relaycorp/awala/keystores/file/FileCertificateStore.kt
index 423d1ae..33ae5d3 100644
--- a/src/main/kotlin/tech/relaycorp/awala/keystores/file/FileCertificateStore.kt
+++ b/src/main/kotlin/tech/relaycorp/awala/keystores/file/FileCertificateStore.kt
@@ -10,8 +10,9 @@ import java.time.ZoneId
import java.time.ZonedDateTime
import tech.relaycorp.relaynet.keystores.CertificateStore
-public class FileCertificateStore(keystoreRoot: FileKeystoreRoot) : CertificateStore() {
-
+public class FileCertificateStore(
+ keystoreRoot: FileKeystoreRoot,
+) : CertificateStore() {
@Suppress("MemberVisibilityCanBePrivate")
public val rootDirectory: File = keystoreRoot.directory.resolve("certificate")
@@ -19,19 +20,20 @@ public class FileCertificateStore(keystoreRoot: FileKeystoreRoot) : CertificateS
subjectId: String,
leafCertificateExpiryDate: ZonedDateTime,
certificationPathData: ByteArray,
- issuerId: String
+ issuerId: String,
) {
val expirationTimestamp = leafCertificateExpiryDate.toTimestamp()
val dataDigest = certificationPathData.toDigest()
- val certFile = getNodeSubdirectory(subjectId, issuerId).resolve(
- "$expirationTimestamp-$dataDigest"
- )
+ val certFile =
+ getNodeSubdirectory(subjectId, issuerId).resolve(
+ "$expirationTimestamp-$dataDigest",
+ )
saveCertificationFile(certFile, certificationPathData)
}
override suspend fun retrieveData(
subjectId: String,
- issuerId: String
+ issuerId: String,
): List {
val certificateFiles =
getNodeSubdirectory(subjectId, issuerId).listFiles()
@@ -57,22 +59,28 @@ public class FileCertificateStore(keystoreRoot: FileKeystoreRoot) : CertificateS
}
@Throws(FileKeystoreException::class)
- override fun delete(subjectId: String, issuerId: String) {
+ override fun delete(
+ subjectId: String,
+ issuerId: String,
+ ) {
val deletionSucceeded =
getNodeSubdirectory(subjectId, issuerId).deleteRecursively()
if (!deletionSucceeded) {
throw FileKeystoreException(
- "Failed to delete node directory for $subjectId"
+ "Failed to delete node directory for $subjectId",
)
}
}
- private fun saveCertificationFile(certFile: File, serialization: ByteArray) {
+ private fun saveCertificationFile(
+ certFile: File,
+ serialization: ByteArray,
+ ) {
val parentDirectory = certFile.parentFile
val wereDirectoriesCreated = parentDirectory.mkdirs()
if (!wereDirectoriesCreated && !parentDirectory.exists()) {
throw FileKeystoreException(
- "Failed to create address directory for certification files"
+ "Failed to create address directory for certification files",
)
}
try {
@@ -85,22 +93,21 @@ public class FileCertificateStore(keystoreRoot: FileKeystoreRoot) : CertificateS
}
}
- private fun retrieveData(file: File): ByteArray {
- return try {
+ private fun retrieveData(file: File): ByteArray =
+ try {
FileInputStream(file).use { it.readBytes() }
} catch (exc: IOException) {
throw FileKeystoreException("Failed to read certification file", exc)
}
- }
- private fun ZonedDateTime.toTimestamp() =
- toInstant().toEpochMilli()
+ private fun ZonedDateTime.toTimestamp() = toInstant().toEpochMilli()
private fun Long.toZonedDateTime() =
ZonedDateTime.ofInstant(Instant.ofEpochMilli(this), ZoneId.of("UTC"))
private fun File.getExpiryDateFromName(): ZonedDateTime =
- name.split("-")
+ name
+ .split("-")
.first()
.toLongOrNull()
?.toZonedDateTime()
@@ -108,12 +115,12 @@ public class FileCertificateStore(keystoreRoot: FileKeystoreRoot) : CertificateS
private fun getNodeSubdirectory(
subjectId: String,
- issuerId: String
- ) =
- rootDirectory.resolve(issuerId).resolve(subjectId)
+ issuerId: String,
+ ) = rootDirectory.resolve(issuerId).resolve(subjectId)
}
internal fun ByteArray.toDigest() =
- MessageDigest.getInstance("SHA-256")
+ MessageDigest
+ .getInstance("SHA-256")
.digest(this)
.joinToString("") { "%02x".format(it) }
diff --git a/src/main/kotlin/tech/relaycorp/awala/keystores/file/FileKeystoreException.kt b/src/main/kotlin/tech/relaycorp/awala/keystores/file/FileKeystoreException.kt
index 0fa022a..d531d0d 100644
--- a/src/main/kotlin/tech/relaycorp/awala/keystores/file/FileKeystoreException.kt
+++ b/src/main/kotlin/tech/relaycorp/awala/keystores/file/FileKeystoreException.kt
@@ -2,5 +2,7 @@ package tech.relaycorp.awala.keystores.file
import tech.relaycorp.relaynet.keystores.KeyStoreBackendException
-public class FileKeystoreException(message: String, cause: Throwable? = null) :
- KeyStoreBackendException(message, cause)
+public class FileKeystoreException(
+ message: String,
+ cause: Throwable? = null,
+) : KeyStoreBackendException(message, cause)
diff --git a/src/main/kotlin/tech/relaycorp/awala/keystores/file/FileKeystoreRoot.kt b/src/main/kotlin/tech/relaycorp/awala/keystores/file/FileKeystoreRoot.kt
index e45c902..2f679cd 100644
--- a/src/main/kotlin/tech/relaycorp/awala/keystores/file/FileKeystoreRoot.kt
+++ b/src/main/kotlin/tech/relaycorp/awala/keystores/file/FileKeystoreRoot.kt
@@ -2,22 +2,24 @@ package tech.relaycorp.awala.keystores.file
import java.io.File
-public class FileKeystoreRoot @Throws(FileKeystoreException::class) constructor(
- internal val directory: File
-) {
- init {
- if (directory.exists()) {
- if (!directory.isDirectory) {
- throw FileKeystoreException("Root '${directory.path}' isn't a directory")
- }
+public class FileKeystoreRoot
+ @Throws(FileKeystoreException::class)
+ constructor(
+ internal val directory: File,
+ ) {
+ init {
+ if (directory.exists()) {
+ if (!directory.isDirectory) {
+ throw FileKeystoreException("Root '${directory.path}' isn't a directory")
+ }
- // Check permissions (read and write operations are always allowed on Windows)
- if (!directory.canRead()) {
- throw FileKeystoreException("Root '${directory.path}' isn't readable")
- }
- if (!directory.canWrite()) {
- throw FileKeystoreException("Root '${directory.path}' isn't writable")
+ // Check permissions (read and write operations are always allowed on Windows)
+ if (!directory.canRead()) {
+ throw FileKeystoreException("Root '${directory.path}' isn't readable")
+ }
+ if (!directory.canWrite()) {
+ throw FileKeystoreException("Root '${directory.path}' isn't writable")
+ }
}
}
}
-}
diff --git a/src/main/kotlin/tech/relaycorp/awala/keystores/file/FilePrivateKeyStore.kt b/src/main/kotlin/tech/relaycorp/awala/keystores/file/FilePrivateKeyStore.kt
index 3d8301a..152a5b6 100644
--- a/src/main/kotlin/tech/relaycorp/awala/keystores/file/FilePrivateKeyStore.kt
+++ b/src/main/kotlin/tech/relaycorp/awala/keystores/file/FilePrivateKeyStore.kt
@@ -7,14 +7,16 @@ import java.io.OutputStream
import tech.relaycorp.relaynet.keystores.PrivateKeyData
import tech.relaycorp.relaynet.keystores.PrivateKeyStore
-public abstract class FilePrivateKeyStore(keystoreRoot: FileKeystoreRoot) : PrivateKeyStore() {
+public abstract class FilePrivateKeyStore(
+ keystoreRoot: FileKeystoreRoot,
+) : PrivateKeyStore() {
@Suppress("MemberVisibilityCanBePrivate")
public val rootDirectory: File = keystoreRoot.directory.resolve("private")
@Suppress("BlockingMethodInNonBlockingContext")
override suspend fun saveIdentityKeyData(
nodeId: String,
- keyData: PrivateKeyData
+ keyData: PrivateKeyData,
) {
val keyFile = getNodeSubdirectory(nodeId).resolve("identity")
saveKeyFile(keyFile, keyData.privateKeyDer)
@@ -51,7 +53,10 @@ public abstract class FilePrivateKeyStore(keystoreRoot: FileKeystoreRoot) : Priv
return retrieveKeyData(boundKeyPath) ?: retrieveKeyData(unboundKeyPath)
}
- private fun saveKeyFile(keyFile: File, serialization: ByteArray) {
+ private fun saveKeyFile(
+ keyFile: File,
+ serialization: ByteArray,
+ ) {
val parentDirectory = keyFile.parentFile
val wereDirectoriesCreated = parentDirectory.mkdirs()
if (!wereDirectoriesCreated && !parentDirectory.exists()) {
@@ -82,18 +87,19 @@ public abstract class FilePrivateKeyStore(keystoreRoot: FileKeystoreRoot) : Priv
private fun resolveSessionKeyFile(
nodeId: String,
keyId: String,
- peerId: String?
+ peerId: String?,
): File {
val nodeSubdirectory = getNodeSubdirectory(nodeId).resolve("session")
- val parentDirectory = if (peerId != null)
- nodeSubdirectory.resolve(peerId)
- else
- nodeSubdirectory
+ val parentDirectory =
+ if (peerId != null) {
+ nodeSubdirectory.resolve(peerId)
+ } else {
+ nodeSubdirectory
+ }
return parentDirectory.resolve(keyId)
}
- private fun getNodeSubdirectory(nodeId: String) =
- rootDirectory.resolve(nodeId)
+ private fun getNodeSubdirectory(nodeId: String) = rootDirectory.resolve(nodeId)
private fun getNodeDirectories() = rootDirectory.listFiles()?.filter(File::isDirectory)
@@ -116,14 +122,18 @@ public abstract class FilePrivateKeyStore(keystoreRoot: FileKeystoreRoot) : Priv
* Delete all the private keys associated with [peerId].
*/
@Throws(FileKeystoreException::class)
- override suspend fun deleteBoundSessionKeys(nodeId: String, peerId: String) {
- val deletionSucceeded = getNodeSubdirectory(nodeId)
- .resolve("session")
- .resolve(peerId)
- .deleteRecursively()
+ override suspend fun deleteBoundSessionKeys(
+ nodeId: String,
+ peerId: String,
+ ) {
+ val deletionSucceeded =
+ getNodeSubdirectory(nodeId)
+ .resolve("session")
+ .resolve(peerId)
+ .deleteRecursively()
if (!deletionSucceeded) {
throw FileKeystoreException(
- "Failed to delete session keys for node $nodeId and peer $peerId"
+ "Failed to delete session keys for node $nodeId and peer $peerId",
)
}
}
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 d673057..8f9a44b 100644
--- a/src/main/kotlin/tech/relaycorp/awala/keystores/file/FileSessionPublicKeystore.kt
+++ b/src/main/kotlin/tech/relaycorp/awala/keystores/file/FileSessionPublicKeystore.kt
@@ -12,7 +12,7 @@ import tech.relaycorp.relaynet.keystores.SessionPublicKeyData
import tech.relaycorp.relaynet.keystores.SessionPublicKeyStore
public class FileSessionPublicKeystore(
- keystoreRoot: FileKeystoreRoot
+ keystoreRoot: FileKeystoreRoot,
) : SessionPublicKeyStore() {
@Suppress("MemberVisibilityCanBePrivate")
public val rootDirectory: File = keystoreRoot.directory.resolve("public")
@@ -20,7 +20,7 @@ public class FileSessionPublicKeystore(
override suspend fun saveKeyData(
keyData: SessionPublicKeyData,
nodeId: String,
- peerId: String
+ peerId: String,
) {
val wasDirectoryCreated = rootDirectory.mkdirs()
if (!wasDirectoryCreated && !rootDirectory.exists()) {
@@ -28,16 +28,17 @@ public class FileSessionPublicKeystore(
}
val keyDataFile = getKeyDataFile(nodeId, peerId)
- val bsonSerialization = BasicOutputBuffer().use { buffer ->
- BsonBinaryWriter(buffer).use {
- it.writeStartDocument()
- it.writeBinaryData("key_id", BsonBinary(keyData.keyId))
- it.writeBinaryData("key_der", BsonBinary(keyData.keyDer))
- it.writeInt32("creation_timestamp", keyData.creationTimestamp.toInt())
- it.writeEndDocument()
+ val bsonSerialization =
+ BasicOutputBuffer().use { buffer ->
+ BsonBinaryWriter(buffer).use {
+ it.writeStartDocument()
+ it.writeBinaryData("key_id", BsonBinary(keyData.keyId))
+ it.writeBinaryData("key_der", BsonBinary(keyData.keyDer))
+ it.writeInt32("creation_timestamp", keyData.creationTimestamp.toInt())
+ it.writeEndDocument()
+ }
+ buffer.toByteArray()
}
- buffer.toByteArray()
- }
try {
keyDataFile.writeBytes(bsonSerialization)
} catch (exc: IOException) {
@@ -45,36 +46,46 @@ public class FileSessionPublicKeystore(
}
}
- override suspend fun retrieveKeyData(nodeId: String, peerId: String): SessionPublicKeyData? {
+ override suspend fun retrieveKeyData(
+ nodeId: String,
+ peerId: String,
+ ): SessionPublicKeyData? {
val keyDataFile = getKeyDataFile(nodeId, peerId)
- val serialization = try {
- keyDataFile.readBytes()
- } catch (exc: IOException) {
- if (keyDataFile.exists()) {
- throw FileKeystoreException("Failed to read key file", exc)
+ val serialization =
+ try {
+ keyDataFile.readBytes()
+ } catch (exc: IOException) {
+ if (keyDataFile.exists()) {
+ throw FileKeystoreException("Failed to read key file", exc)
+ }
+ return null
}
- return null
- }
- val data = try {
- BsonBinaryReader(ByteBuffer.wrap(serialization)).use {
- it.readStartDocument()
- SessionPublicKeyData(
- it.readBinaryData("key_id").data,
- it.readBinaryData("key_der").data,
- it.readInt32("creation_timestamp").toLong()
- )
+ val data =
+ try {
+ BsonBinaryReader(ByteBuffer.wrap(serialization)).use {
+ it.readStartDocument()
+ SessionPublicKeyData(
+ it.readBinaryData("key_id").data,
+ it.readBinaryData("key_der").data,
+ it.readInt32("creation_timestamp").toLong(),
+ )
+ }
+ } catch (exc: BSONException) {
+ throw FileKeystoreException("Key file is malformed", exc)
}
- } catch (exc: BSONException) {
- throw FileKeystoreException("Key file is malformed", exc)
- }
return data
}
- override suspend fun delete(nodeId: String, peerId: String) {
+ override suspend fun delete(
+ nodeId: String,
+ peerId: String,
+ ) {
val keyDataFile = getKeyDataFile(nodeId, peerId)
keyDataFile.delete()
}
- private fun getKeyDataFile(nodeId: String, peerId: String) =
- rootDirectory.resolve("$nodeId-$peerId")
+ private fun getKeyDataFile(
+ nodeId: String,
+ peerId: String,
+ ) = rootDirectory.resolve("$nodeId-$peerId")
}
diff --git a/src/test/kotlin/tech/relaycorp/awala/keystores/file/FileCertificateStoreTest.kt b/src/test/kotlin/tech/relaycorp/awala/keystores/file/FileCertificateStoreTest.kt
index de63219..ea47d8c 100644
--- a/src/test/kotlin/tech/relaycorp/awala/keystores/file/FileCertificateStoreTest.kt
+++ b/src/test/kotlin/tech/relaycorp/awala/keystores/file/FileCertificateStoreTest.kt
@@ -20,33 +20,35 @@ import tech.relaycorp.relaynet.testing.pki.PDACertPath
@ExperimentalCoroutinesApi
@Suppress("BlockingMethodInNonBlockingContext")
internal class FileCertificateStoreTest : KeystoreTestCase() {
-
private val storeRootFile = keystoreRoot.directory.resolve("certificate")
- private val certificate = issueGatewayCertificate(
- KeyPairSet.PRIVATE_GW.public,
- KeyPairSet.INTERNET_GW.private,
- validityEndDate = ZonedDateTime.now().plusHours(1),
- validityStartDate = ZonedDateTime.now().minusSeconds(1)
- )
+ private val certificate =
+ issueGatewayCertificate(
+ KeyPairSet.PRIVATE_GW.public,
+ KeyPairSet.INTERNET_GW.private,
+ validityEndDate = ZonedDateTime.now().plusHours(1),
+ validityStartDate = ZonedDateTime.now().minusSeconds(1),
+ )
private val chain = listOf(PDACertPath.INTERNET_GW)
private val issuerId = PDACertPath.INTERNET_GW.subjectId
private val subjectId = certificate.subjectId
private val issuerFolder = storeRootFile.resolve(issuerId)
private val subjectFolder = issuerFolder.resolve(subjectId)
- private val longerCertificate = issueGatewayCertificate(
- KeyPairSet.PRIVATE_GW.public,
- KeyPairSet.INTERNET_GW.private,
- validityEndDate = ZonedDateTime.now().plusHours(10),
- validityStartDate = ZonedDateTime.now().minusSeconds(1)
- )
- private val aboutToExpireCertificate = issueGatewayCertificate(
- KeyPairSet.PRIVATE_GW.public,
- KeyPairSet.INTERNET_GW.private,
- validityEndDate = ZonedDateTime.now().plusNanos(100_000_000),
- validityStartDate = ZonedDateTime.now().minusSeconds(1)
- )
+ private val longerCertificate =
+ issueGatewayCertificate(
+ KeyPairSet.PRIVATE_GW.public,
+ KeyPairSet.INTERNET_GW.private,
+ validityEndDate = ZonedDateTime.now().plusHours(10),
+ validityStartDate = ZonedDateTime.now().minusSeconds(1),
+ )
+ private val aboutToExpireCertificate =
+ issueGatewayCertificate(
+ KeyPairSet.PRIVATE_GW.public,
+ KeyPairSet.INTERNET_GW.private,
+ validityEndDate = ZonedDateTime.now().plusNanos(100_000_000),
+ validityStartDate = ZonedDateTime.now().minusSeconds(1),
+ )
private val unrelatedCertificate = PDACertPath.PRIVATE_ENDPOINT
@Test
@@ -59,168 +61,193 @@ internal class FileCertificateStoreTest : KeystoreTestCase() {
@Nested
inner class SaveData {
@Test
- fun `Certificate should be stored and retrieved`() = runTest {
- val keystore = FileCertificateStore(keystoreRoot)
-
- keystore.save(CertificationPath(certificate, chain), issuerId)
-
- val result = keystore.retrieveAll(subjectId, issuerId)
- assertEquals(1, result.size)
- assertEquals(
- certificate.serialize().asList(),
- result.first().leafCertificate.serialize().asList()
- )
- assertEquals(
- chain.map { it.serialize().asList() },
- result.first().certificateAuthorities.map { it.serialize().asList() }
- )
-
- val addressFiles = subjectFolder.listFiles()!!
- assertEquals(1, addressFiles.size)
- with(addressFiles.first()) {
- assertTrue(exists())
- assertTrue(
- name.startsWith("${certificate.expiryDate.toInstant().toEpochMilli()}-")
+ fun `Certificate should be stored and retrieved`() =
+ runTest {
+ val keystore = FileCertificateStore(keystoreRoot)
+
+ keystore.save(CertificationPath(certificate, chain), issuerId)
+
+ val result = keystore.retrieveAll(subjectId, issuerId)
+ assertEquals(1, result.size)
+ assertEquals(
+ certificate.serialize().asList(),
+ result
+ .first()
+ .leafCertificate
+ .serialize()
+ .asList(),
)
+ assertEquals(
+ chain.map { it.serialize().asList() },
+ result.first().certificateAuthorities.map { it.serialize().asList() },
+ )
+
+ val addressFiles = subjectFolder.listFiles()!!
+ assertEquals(1, addressFiles.size)
+ with(addressFiles.first()) {
+ assertTrue(exists())
+ assertTrue(
+ name.startsWith("${certificate.expiryDate.toInstant().toEpochMilli()}-"),
+ )
+ }
}
- }
@Test
- fun `Certificate stored multiple times should override`() = runTest {
- val keystore = FileCertificateStore(keystoreRoot)
+ fun `Certificate stored multiple times should override`() =
+ runTest {
+ val keystore = FileCertificateStore(keystoreRoot)
- repeat(3) {
- keystore.save(CertificationPath(certificate, chain), issuerId)
- }
+ repeat(3) {
+ keystore.save(CertificationPath(certificate, chain), issuerId)
+ }
- val result = keystore.retrieveAll(subjectId, issuerId)
- assertEquals(1, result.size)
- assertEquals(
- certificate.serialize().asList(),
- result.first().leafCertificate.serialize().asList()
- )
- assertEquals(
- chain.map { it.serialize().asList() },
- result.first().certificateAuthorities.map { it.serialize().asList() }
- )
-
- val addressFiles = subjectFolder.listFiles()!!
- assertEquals(1, addressFiles.size)
- }
+ val result = keystore.retrieveAll(subjectId, issuerId)
+ assertEquals(1, result.size)
+ assertEquals(
+ certificate.serialize().asList(),
+ result
+ .first()
+ .leafCertificate
+ .serialize()
+ .asList(),
+ )
+ assertEquals(
+ chain.map { it.serialize().asList() },
+ result.first().certificateAuthorities.map { it.serialize().asList() },
+ )
- @Test
- internal fun `Certificates by different issuers should not override`() = runTest {
- val keystore = FileCertificateStore(keystoreRoot)
-
- keystore.save(CertificationPath(certificate, chain), issuerId)
- keystore.save(CertificationPath(certificate, emptyList()), issuerId + "diff")
-
- val result = keystore.retrieveAll(subjectId, issuerId)
- assertEquals(1, result.size)
- assertEquals(
- certificate.serialize().asList(),
- result.first().leafCertificate.serialize().asList()
- )
- assertEquals(
- chain.map { it.serialize().asList() },
- result.first().certificateAuthorities.map { it.serialize().asList() }
- )
-
- val resultDiff = keystore.retrieveAll(subjectId, issuerId + "diff")
- assertEquals(1, resultDiff.size)
- assertEquals(
- certificate.serialize().asList(),
- resultDiff.first().leafCertificate.serialize().asList()
- )
- assertTrue(
- resultDiff.first().certificateAuthorities.isEmpty()
- )
- }
+ val addressFiles = subjectFolder.listFiles()!!
+ assertEquals(1, addressFiles.size)
+ }
@Test
- @DisabledOnOs(OS.WINDOWS)
- fun `Errors creating address subdirectory should be wrapped`() = runTest {
- keystoreRoot.directory.setExecutable(false)
- keystoreRoot.directory.setWritable(false)
- val keystore = FileCertificateStore(keystoreRoot)
+ internal fun `Certificates by different issuers should not override`() =
+ runTest {
+ val keystore = FileCertificateStore(keystoreRoot)
- val exception = assertThrows {
keystore.save(CertificationPath(certificate, chain), issuerId)
- }
+ keystore.save(CertificationPath(certificate, emptyList()), issuerId + "diff")
- assertEquals(
- "Failed to create address directory for certification files",
- exception.message
- )
- }
+ val result = keystore.retrieveAll(subjectId, issuerId)
+ assertEquals(1, result.size)
+ assertEquals(
+ certificate.serialize().asList(),
+ result
+ .first()
+ .leafCertificate
+ .serialize()
+ .asList(),
+ )
+ assertEquals(
+ chain.map { it.serialize().asList() },
+ result.first().certificateAuthorities.map { it.serialize().asList() },
+ )
+
+ val resultDiff = keystore.retrieveAll(subjectId, issuerId + "diff")
+ assertEquals(1, resultDiff.size)
+ assertEquals(
+ certificate.serialize().asList(),
+ resultDiff
+ .first()
+ .leafCertificate
+ .serialize()
+ .asList(),
+ )
+ assertTrue(
+ resultDiff.first().certificateAuthorities.isEmpty(),
+ )
+ }
@Test
@DisabledOnOs(OS.WINDOWS)
- fun `Errors creating or updating file should be wrapped`() = runTest {
- subjectFolder.mkdirs()
- subjectFolder.setWritable(false)
- val keystore = FileCertificateStore(keystoreRoot)
+ fun `Errors creating address subdirectory should be wrapped`() =
+ runTest {
+ keystoreRoot.directory.setExecutable(false)
+ keystoreRoot.directory.setWritable(false)
+ val keystore = FileCertificateStore(keystoreRoot)
- val exception = assertThrows {
- keystore.save(CertificationPath(certificate, chain), issuerId)
+ val exception =
+ assertThrows {
+ keystore.save(CertificationPath(certificate, chain), issuerId)
+ }
+
+ assertEquals(
+ "Failed to create address directory for certification files",
+ exception.message,
+ )
}
- assertEquals("Failed to save certification file", exception.message)
- assertTrue(exception.cause is IOException)
- }
+ @Test
+ @DisabledOnOs(OS.WINDOWS)
+ fun `Errors creating or updating file should be wrapped`() =
+ runTest {
+ subjectFolder.mkdirs()
+ subjectFolder.setWritable(false)
+ val keystore = FileCertificateStore(keystoreRoot)
+
+ val exception =
+ assertThrows {
+ keystore.save(CertificationPath(certificate, chain), issuerId)
+ }
+
+ assertEquals("Failed to save certification file", exception.message)
+ assertTrue(exception.cause is IOException)
+ }
}
@Nested
inner class RetrieveData {
-
@Test
- fun `All valid certificates should be retrieved`() = runTest {
- val keystore = FileCertificateStore(keystoreRoot)
-
- keystore.save(CertificationPath(certificate, chain), issuerId)
- keystore.save(CertificationPath(longerCertificate, chain), issuerId)
- keystore.save(CertificationPath(aboutToExpireCertificate, chain), issuerId)
-
- Thread.sleep(300) // wait for aboutToExpireCertificate to expire
-
- val result = keystore.retrieveAll(subjectId, issuerId)
- assertEquals(2, result.size)
- assertTrue(
- result.any { certPath ->
- certificate.serialize().asList() ==
- certPath.leafCertificate.serialize().asList() &&
- chain.map { it.serialize().asList() } ==
- certPath.certificateAuthorities.map { it.serialize().asList() }
- }
- )
-
- assertTrue(
- result.any { certPath ->
- longerCertificate.serialize().asList() ==
- certPath.leafCertificate.serialize().asList() &&
- chain.map { it.serialize().asList() } ==
- certPath.certificateAuthorities.map { it.serialize().asList() }
- }
- )
- }
+ fun `All valid certificates should be retrieved`() =
+ runTest {
+ val keystore = FileCertificateStore(keystoreRoot)
+
+ keystore.save(CertificationPath(certificate, chain), issuerId)
+ keystore.save(CertificationPath(longerCertificate, chain), issuerId)
+ keystore.save(CertificationPath(aboutToExpireCertificate, chain), issuerId)
+
+ Thread.sleep(300) // wait for aboutToExpireCertificate to expire
+
+ val result = keystore.retrieveAll(subjectId, issuerId)
+ assertEquals(2, result.size)
+ assertTrue(
+ result.any { certPath ->
+ certificate.serialize().asList() ==
+ certPath.leafCertificate.serialize().asList() &&
+ chain.map { it.serialize().asList() } ==
+ certPath.certificateAuthorities.map { it.serialize().asList() }
+ },
+ )
+
+ assertTrue(
+ result.any { certPath ->
+ longerCertificate.serialize().asList() ==
+ certPath.leafCertificate.serialize().asList() &&
+ chain.map { it.serialize().asList() } ==
+ certPath.certificateAuthorities.map { it.serialize().asList() }
+ },
+ )
+ }
@Test
- fun `Certificates should not be retrieved with wrong issuer`() = runTest {
- val keystore = FileCertificateStore(keystoreRoot)
+ fun `Certificates should not be retrieved with wrong issuer`() =
+ runTest {
+ val keystore = FileCertificateStore(keystoreRoot)
- keystore.save(CertificationPath(certificate, chain), issuerId)
+ keystore.save(CertificationPath(certificate, chain), issuerId)
- val result = keystore.retrieveAll(subjectId, "wrong")
- assertTrue(result.isEmpty())
- }
+ val result = keystore.retrieveAll(subjectId, "wrong")
+ assertTrue(result.isEmpty())
+ }
@Test
- fun `If there are no certificates return empty list`() = runTest {
- val keystore = FileCertificateStore(keystoreRoot)
+ fun `If there are no certificates return empty list`() =
+ runTest {
+ val keystore = FileCertificateStore(keystoreRoot)
- val result = keystore.retrieveAll(subjectId, issuerId)
- assertTrue(result.isEmpty())
- }
+ val result = keystore.retrieveAll(subjectId, issuerId)
+ assertTrue(result.isEmpty())
+ }
@Test
@DisabledOnOs(OS.WINDOWS) // Windows can't tell apart between not-readable and non-existing
@@ -234,12 +261,13 @@ internal class FileCertificateStoreTest : KeystoreTestCase() {
file.createNewFile()
file.setReadable(false)
- val exception = assertThrows {
- keystore.retrieveAll(subjectId, issuerId)
- }
+ val exception =
+ assertThrows {
+ keystore.retrieveAll(subjectId, issuerId)
+ }
assertEquals(
"Failed to read certification file",
- exception.message
+ exception.message,
)
}
@@ -251,12 +279,13 @@ internal class FileCertificateStoreTest : KeystoreTestCase() {
subjectFolder.mkdirs()
subjectFolder.resolve("INVALID").createNewFile()
- val exception = assertThrows {
- keystore.retrieveAll(subjectId, issuerId)
- }
+ val exception =
+ assertThrows {
+ keystore.retrieveAll(subjectId, issuerId)
+ }
assertEquals(
"Invalid certificate file name: INVALID",
- exception.message
+ exception.message,
)
}
@@ -268,138 +297,145 @@ internal class FileCertificateStoreTest : KeystoreTestCase() {
subjectFolder.mkdirs()
subjectFolder.resolve("AAA-AAA").createNewFile()
- val exception = assertThrows {
- keystore.retrieveAll(subjectId, issuerId)
- }
+ val exception =
+ assertThrows {
+ keystore.retrieveAll(subjectId, issuerId)
+ }
assertEquals(
"Invalid certificate file name: AAA-AAA",
- exception.message
+ exception.message,
)
}
}
@Nested
inner class DeleteExpired {
-
@Test
- fun `Certificates that are expired are deleted`() = runTest {
- val keystore = FileCertificateStore(keystoreRoot)
+ fun `Certificates that are expired are deleted`() =
+ runTest {
+ val keystore = FileCertificateStore(keystoreRoot)
- keystore.save(CertificationPath(certificate, chain), issuerId)
- // create empty expired cert file
- subjectFolder.resolve("0-12345").createNewFile()
+ keystore.save(CertificationPath(certificate, chain), issuerId)
+ // create empty expired cert file
+ subjectFolder.resolve("0-12345").createNewFile()
- assertEquals(2, subjectFolder.listFiles()!!.size)
+ assertEquals(2, subjectFolder.listFiles()!!.size)
- keystore.deleteExpired()
+ keystore.deleteExpired()
- assertEquals(1, subjectFolder.listFiles()!!.size)
- }
+ assertEquals(1, subjectFolder.listFiles()!!.size)
+ }
@Test
- fun `Skip if root folder couldn't be read`() = runTest {
- val keystore = FileCertificateStore(keystoreRoot)
+ fun `Skip if root folder couldn't be read`() =
+ runTest {
+ val keystore = FileCertificateStore(keystoreRoot)
- storeRootFile.setReadable(false)
+ storeRootFile.setReadable(false)
- keystore.deleteExpired()
- storeRootFile.setReadable(true)
- }
+ keystore.deleteExpired()
+ storeRootFile.setReadable(true)
+ }
@Test
- fun `Skip if issuer folder couldn't be read`() = runTest {
- val keystore = FileCertificateStore(keystoreRoot)
+ fun `Skip if issuer folder couldn't be read`() =
+ runTest {
+ val keystore = FileCertificateStore(keystoreRoot)
- issuerFolder.mkdirs()
- issuerFolder.setReadable(false)
+ issuerFolder.mkdirs()
+ issuerFolder.setReadable(false)
- keystore.deleteExpired()
- issuerFolder.setReadable(true)
- }
+ keystore.deleteExpired()
+ issuerFolder.setReadable(true)
+ }
@Test
- fun `Skip if address folder couldn't be read`() = runTest {
- val keystore = FileCertificateStore(keystoreRoot)
+ fun `Skip if address folder couldn't be read`() =
+ runTest {
+ val keystore = FileCertificateStore(keystoreRoot)
- subjectFolder.mkdirs()
- subjectFolder.setReadable(false)
+ subjectFolder.mkdirs()
+ subjectFolder.setReadable(false)
- keystore.deleteExpired()
- subjectFolder.setReadable(true)
- }
+ keystore.deleteExpired()
+ subjectFolder.setReadable(true)
+ }
@Test
- fun `Skip files inside root folder`() = runTest {
- val keystore = FileCertificateStore(keystoreRoot)
+ fun `Skip files inside root folder`() =
+ runTest {
+ val keystore = FileCertificateStore(keystoreRoot)
- storeRootFile.mkdirs()
- storeRootFile.resolve("file").createNewFile()
+ storeRootFile.mkdirs()
+ storeRootFile.resolve("file").createNewFile()
- keystore.deleteExpired()
- }
+ keystore.deleteExpired()
+ }
@Test
- fun `Skip files inside issuer folder`() = runTest {
- val keystore = FileCertificateStore(keystoreRoot)
+ fun `Skip files inside issuer folder`() =
+ runTest {
+ val keystore = FileCertificateStore(keystoreRoot)
- issuerFolder.mkdirs()
- issuerFolder.resolve("file").createNewFile()
+ issuerFolder.mkdirs()
+ issuerFolder.resolve("file").createNewFile()
- keystore.deleteExpired()
- }
+ keystore.deleteExpired()
+ }
@Test
- fun `Skip if expired certificate couldn't be deleted`() = runTest {
- val keystore = FileCertificateStore(keystoreRoot)
+ fun `Skip if expired certificate couldn't be deleted`() =
+ runTest {
+ val keystore = FileCertificateStore(keystoreRoot)
- subjectFolder.mkdirs()
- val file = subjectFolder.resolve("0-12345")
- file.createNewFile()
- storeRootFile.setWritable(false)
+ subjectFolder.mkdirs()
+ val file = subjectFolder.resolve("0-12345")
+ file.createNewFile()
+ storeRootFile.setWritable(false)
- keystore.deleteExpired()
- }
+ keystore.deleteExpired()
+ }
}
@Nested
inner class Delete {
-
@Test
- fun `Certificates of given subject and issuer addresses are deleted`() = runTest {
- val keystore = FileCertificateStore(keystoreRoot)
-
- keystore.save(CertificationPath(certificate, chain), issuerId)
- keystore.save(CertificationPath(unrelatedCertificate, chain), issuerId)
- keystore.save(CertificationPath(certificate, chain), issuerId + "diff")
-
- assertEquals(
- 1,
- keystore.retrieveAll(certificate.subjectId, issuerId).size
- )
- assertEquals(
- 1,
- keystore.retrieveAll(unrelatedCertificate.subjectId, issuerId).size
- )
- assertEquals(
- 1,
- keystore.retrieveAll(certificate.subjectId, issuerId + "diff").size
- )
-
- keystore.delete(certificate.subjectId, issuerId)
-
- assertEquals(
- 0,
- keystore.retrieveAll(certificate.subjectId, issuerId).size
- )
- assertEquals(
- 1,
- keystore.retrieveAll(unrelatedCertificate.subjectId, issuerId).size
- )
- assertEquals(
- 1,
- keystore.retrieveAll(certificate.subjectId, issuerId + "diff").size
- )
- }
+ fun `Certificates of given subject and issuer addresses are deleted`() =
+ runTest {
+ val keystore = FileCertificateStore(keystoreRoot)
+
+ keystore.save(CertificationPath(certificate, chain), issuerId)
+ keystore.save(CertificationPath(unrelatedCertificate, chain), issuerId)
+ keystore.save(CertificationPath(certificate, chain), issuerId + "diff")
+
+ assertEquals(
+ 1,
+ keystore.retrieveAll(certificate.subjectId, issuerId).size,
+ )
+ assertEquals(
+ 1,
+ keystore.retrieveAll(unrelatedCertificate.subjectId, issuerId).size,
+ )
+ assertEquals(
+ 1,
+ keystore.retrieveAll(certificate.subjectId, issuerId + "diff").size,
+ )
+
+ keystore.delete(certificate.subjectId, issuerId)
+
+ assertEquals(
+ 0,
+ keystore.retrieveAll(certificate.subjectId, issuerId).size,
+ )
+ assertEquals(
+ 1,
+ keystore.retrieveAll(unrelatedCertificate.subjectId, issuerId).size,
+ )
+ assertEquals(
+ 1,
+ keystore.retrieveAll(certificate.subjectId, issuerId + "diff").size,
+ )
+ }
@Test
@DisabledOnOs(OS.WINDOWS) // Windows can't tell apart between not-writable and non-existing
@@ -410,12 +446,13 @@ internal class FileCertificateStoreTest : KeystoreTestCase() {
subjectFolder.mkdirs()
issuerFolder.setWritable(false)
- val exception = assertThrows {
- keystore.delete(subjectId, issuerId)
- }
+ val exception =
+ assertThrows {
+ keystore.delete(subjectId, issuerId)
+ }
assertEquals(
"Failed to delete node directory for $subjectId",
- exception.message
+ exception.message,
)
}
@@ -429,7 +466,7 @@ internal class FileCertificateStoreTest : KeystoreTestCase() {
assertEquals(
1,
- keystore.retrieveAll(certificate.subjectId, issuerId).size
+ keystore.retrieveAll(certificate.subjectId, issuerId).size,
)
}
}
diff --git a/src/test/kotlin/tech/relaycorp/awala/keystores/file/FileKeystoreRootTest.kt b/src/test/kotlin/tech/relaycorp/awala/keystores/file/FileKeystoreRootTest.kt
index 04633dc..a7471e7 100644
--- a/src/test/kotlin/tech/relaycorp/awala/keystores/file/FileKeystoreRootTest.kt
+++ b/src/test/kotlin/tech/relaycorp/awala/keystores/file/FileKeystoreRootTest.kt
@@ -33,7 +33,8 @@ class FileKeystoreRootTest {
if (rootDirectoryPath.exists()) {
rootDirectoryPath.toFile().setReadable(true)
- Files.walk(rootDirectoryPath)
+ Files
+ .walk(rootDirectoryPath)
.sorted(Comparator.reverseOrder())
.map(Path::toFile)
.forEach(File::delete)
@@ -52,13 +53,14 @@ class FileKeystoreRootTest {
val file = rootDirectoryFile.resolve("file.txt")
file.createNewFile()
- val exception = assertThrows {
- FileKeystoreRoot(file)
- }
+ val exception =
+ assertThrows {
+ FileKeystoreRoot(file)
+ }
assertEquals(
"Root '${file.path}' isn't a directory",
- exception.message
+ exception.message,
)
}
@@ -88,13 +90,14 @@ class FileKeystoreRootTest {
fun `Root directory should be refused if it isn't readable`() {
rootDirectoryPath.toFile().setReadable(false)
- val exception = assertThrows {
- FileKeystoreRoot(rootDirectoryFile)
- }
+ val exception =
+ assertThrows {
+ FileKeystoreRoot(rootDirectoryFile)
+ }
assertEquals(
"Root '${rootDirectoryPath.pathString}' isn't readable",
- exception.message
+ exception.message,
)
}
@@ -103,13 +106,14 @@ class FileKeystoreRootTest {
fun `Root directory should be refused if it isn't writable`() {
rootDirectoryPath.toFile().setWritable(false)
- val exception = assertThrows {
- FileKeystoreRoot(rootDirectoryFile)
- }
+ val exception =
+ assertThrows {
+ FileKeystoreRoot(rootDirectoryFile)
+ }
assertEquals(
"Root '${rootDirectoryPath.pathString}' isn't writable",
- exception.message
+ exception.message,
)
}
}
diff --git a/src/test/kotlin/tech/relaycorp/awala/keystores/file/FilePrivateKeyStoreTest.kt b/src/test/kotlin/tech/relaycorp/awala/keystores/file/FilePrivateKeyStoreTest.kt
index 923ac84..d33e789 100644
--- a/src/test/kotlin/tech/relaycorp/awala/keystores/file/FilePrivateKeyStoreTest.kt
+++ b/src/test/kotlin/tech/relaycorp/awala/keystores/file/FilePrivateKeyStoreTest.kt
@@ -35,11 +35,12 @@ class FilePrivateKeyStoreTest : KeystoreTestCase() {
private val identityKeyFilePath = nodeDirectoryPath.resolve("identity")
private val boundSessionKeyFilePath =
nodeDirectoryPath.resolve("session").resolve(peerId).resolve(
- byteArrayToHex(sessionKeypair.sessionKey.keyId)
+ byteArrayToHex(sessionKeypair.sessionKey.keyId),
+ )
+ private val unboundSessionKeyFilePath =
+ nodeDirectoryPath.resolve("session").resolve(
+ byteArrayToHex(sessionKeypair.sessionKey.keyId),
)
- private val unboundSessionKeyFilePath = nodeDirectoryPath.resolve("session").resolve(
- byteArrayToHex(sessionKeypair.sessionKey.keyId)
- )
@Test
fun `Root directory should be exposed`() {
@@ -49,176 +50,186 @@ class FilePrivateKeyStoreTest : KeystoreTestCase() {
}
@Nested
- inner class SaveIdentity : PrivateKeyStoreSavingTestCase(
- keystoreRoot,
- identityKeyFilePath,
- { saveIdentityKey(privateKey) }
- ) {
-
+ inner class SaveIdentity :
+ PrivateKeyStoreSavingTestCase(
+ keystoreRoot,
+ identityKeyFilePath,
+ { saveIdentityKey(privateKey) },
+ ) {
@Test
- override fun `Private key should be stored`() = runTest {
- val keystore = MockFilePrivateKeyStore(keystoreRoot)
+ override fun `Private key should be stored`() =
+ runTest {
+ val keystore = MockFilePrivateKeyStore(keystoreRoot)
- keystore.saveIdentityKey(privateKey)
+ keystore.saveIdentityKey(privateKey)
- val savedKeyData = readKeyData(identityKeyFilePath)
- assertEquals(
- privateKey.encoded.asList(),
- savedKeyData.asList()
- )
- }
+ val savedKeyData = readKeyData(identityKeyFilePath)
+ assertEquals(
+ privateKey.encoded.asList(),
+ savedKeyData.asList(),
+ )
+ }
- private fun readKeyData(path: Path) =
- MockFilePrivateKeyStore.readFile(path.toFile())
+ private fun readKeyData(path: Path) = MockFilePrivateKeyStore.readFile(path.toFile())
}
@Nested
- inner class RetrieveIdentity : PrivateKeyStoreRetrievalTestCase(
- keystoreRoot,
- identityKeyFilePath,
- { retrieveIdentityKey(privateId) }
- ) {
-
+ inner class RetrieveIdentity :
+ PrivateKeyStoreRetrievalTestCase(
+ keystoreRoot,
+ identityKeyFilePath,
+ { retrieveIdentityKey(privateId) },
+ ) {
@Test
- fun `Exception should be thrown if private key does not exist`() = runTest {
- val keystore = MockFilePrivateKeyStore(keystoreRoot)
+ fun `Exception should be thrown if private key does not exist`() =
+ runTest {
+ val keystore = MockFilePrivateKeyStore(keystoreRoot)
- val exception = assertThrows {
- keystore.retrieveIdentityKey(privateId)
- }
+ val exception =
+ assertThrows {
+ keystore.retrieveIdentityKey(privateId)
+ }
- assertEquals("There is no identity key for $privateId", exception.message)
- }
+ assertEquals("There is no identity key for $privateId", exception.message)
+ }
@Test
- override fun `Private key should be returned if file exists`() = runTest {
- val keystore = MockFilePrivateKeyStore(keystoreRoot)
- keystore.saveIdentityKey(privateKey)
+ override fun `Private key should be returned if file exists`() =
+ runTest {
+ val keystore = MockFilePrivateKeyStore(keystoreRoot)
+ keystore.saveIdentityKey(privateKey)
- val key = keystore.retrieveIdentityKey(privateId)
+ val key = keystore.retrieveIdentityKey(privateId)
- assertEquals(privateKey, key)
- }
+ assertEquals(privateKey, key)
+ }
}
@Nested
inner class AllIdentityKeys {
@Test
- fun `Nothing should be returned if store is empty`() = runTest {
- val keystore = MockFilePrivateKeyStore(keystoreRoot)
+ fun `Nothing should be returned if store is empty`() =
+ runTest {
+ val keystore = MockFilePrivateKeyStore(keystoreRoot)
- val allIdentityKeys = keystore.retrieveAllIdentityKeys()
+ val allIdentityKeys = keystore.retrieveAllIdentityKeys()
- assertEquals(0, allIdentityKeys.size)
- }
+ assertEquals(0, allIdentityKeys.size)
+ }
@Test
- fun `All identity key pairs should be returned`() = runTest {
- val keystore = MockFilePrivateKeyStore(keystoreRoot)
- keystore.saveIdentityKey(privateKey)
- val extraPrivateKey = KeyPairSet.PDA_GRANTEE.private
- keystore.saveIdentityKey(extraPrivateKey)
+ fun `All identity key pairs should be returned`() =
+ runTest {
+ val keystore = MockFilePrivateKeyStore(keystoreRoot)
+ keystore.saveIdentityKey(privateKey)
+ val extraPrivateKey = KeyPairSet.PDA_GRANTEE.private
+ keystore.saveIdentityKey(extraPrivateKey)
- val allIdentityKeys = keystore.retrieveAllIdentityKeys()
+ val allIdentityKeys = keystore.retrieveAllIdentityKeys()
- assertEquals(2, allIdentityKeys.size)
- assertContains(allIdentityKeys, privateKey)
- assertContains(allIdentityKeys, extraPrivateKey)
- }
+ assertEquals(2, allIdentityKeys.size)
+ assertContains(allIdentityKeys, privateKey)
+ assertContains(allIdentityKeys, extraPrivateKey)
+ }
@Test
- fun `Irrelevant subdirectories should be ignored`() = runTest {
- val keystore = MockFilePrivateKeyStore(keystoreRoot)
- keystore.saveIdentityKey(privateKey)
- privateKeystoreRootFile.resolve("invalid").toPath().createDirectories()
+ fun `Irrelevant subdirectories should be ignored`() =
+ runTest {
+ val keystore = MockFilePrivateKeyStore(keystoreRoot)
+ keystore.saveIdentityKey(privateKey)
+ privateKeystoreRootFile.resolve("invalid").toPath().createDirectories()
- val allIdentityKeys = keystore.retrieveAllIdentityKeys()
+ val allIdentityKeys = keystore.retrieveAllIdentityKeys()
- assertEquals(1, allIdentityKeys.size)
- assertContains(allIdentityKeys, privateKey)
- }
+ assertEquals(1, allIdentityKeys.size)
+ assertContains(allIdentityKeys, privateKey)
+ }
@Test
- fun `Irrelevant files should be ignored`() = runTest {
- val keystore = MockFilePrivateKeyStore(keystoreRoot)
- keystore.saveIdentityKey(privateKey)
- privateKeystoreRootFile.resolve("invalid").createNewFile()
+ fun `Irrelevant files should be ignored`() =
+ runTest {
+ val keystore = MockFilePrivateKeyStore(keystoreRoot)
+ keystore.saveIdentityKey(privateKey)
+ privateKeystoreRootFile.resolve("invalid").createNewFile()
- val allIdentityKeys = keystore.retrieveAllIdentityKeys()
+ val allIdentityKeys = keystore.retrieveAllIdentityKeys()
- assertEquals(1, allIdentityKeys.size)
- assertContains(allIdentityKeys, privateKey)
- }
+ assertEquals(1, allIdentityKeys.size)
+ assertContains(allIdentityKeys, privateKey)
+ }
}
@Nested
- inner class SaveSession : PrivateKeyStoreSavingTestCase(
- keystoreRoot,
- unboundSessionKeyFilePath,
- {
- saveSessionKey(
- sessionKeypair.privateKey,
- sessionKeypair.sessionKey.keyId,
- privateId
- )
- }
- ) {
-
+ inner class SaveSession :
+ PrivateKeyStoreSavingTestCase(
+ keystoreRoot,
+ unboundSessionKeyFilePath,
+ {
+ saveSessionKey(
+ sessionKeypair.privateKey,
+ sessionKeypair.sessionKey.keyId,
+ privateId,
+ )
+ },
+ ) {
@Test
- override fun `Private key should be stored`() = runTest {
- val keystore = MockFilePrivateKeyStore(keystoreRoot)
+ override fun `Private key should be stored`() =
+ runTest {
+ val keystore = MockFilePrivateKeyStore(keystoreRoot)
- keystore.saveSessionKey(
- sessionKeypair.privateKey,
- sessionKeypair.sessionKey.keyId,
- privateId
- )
+ keystore.saveSessionKey(
+ sessionKeypair.privateKey,
+ sessionKeypair.sessionKey.keyId,
+ privateId,
+ )
- assertEquals(
- sessionKeypair.privateKey.encoded.asList(),
- readKeyData(unboundSessionKeyFilePath).asList()
- )
- }
+ assertEquals(
+ sessionKeypair.privateKey.encoded.asList(),
+ readKeyData(unboundSessionKeyFilePath).asList(),
+ )
+ }
@Test
- fun `Existing file should be updated if key already existed`() = runTest {
- val keystore = MockFilePrivateKeyStore(keystoreRoot)
- keystore.saveSessionKey(
- sessionKeypair.privateKey,
- sessionKeypair.sessionKey.keyId,
- privateId
- )
-
- // Replace the private key
- val differentSessionKeyPair = SessionKeyPair.generate()
- keystore.saveSessionKey(
- differentSessionKeyPair.privateKey,
- sessionKeypair.sessionKey.keyId,
- privateId
- )
-
- assertEquals(
- differentSessionKeyPair.privateKey.encoded.asList(),
- readKeyData(unboundSessionKeyFilePath).asList()
- )
- }
+ fun `Existing file should be updated if key already existed`() =
+ runTest {
+ val keystore = MockFilePrivateKeyStore(keystoreRoot)
+ keystore.saveSessionKey(
+ sessionKeypair.privateKey,
+ sessionKeypair.sessionKey.keyId,
+ privateId,
+ )
+
+ // Replace the private key
+ val differentSessionKeyPair = SessionKeyPair.generate()
+ keystore.saveSessionKey(
+ differentSessionKeyPair.privateKey,
+ sessionKeypair.sessionKey.keyId,
+ privateId,
+ )
+
+ assertEquals(
+ differentSessionKeyPair.privateKey.encoded.asList(),
+ readKeyData(unboundSessionKeyFilePath).asList(),
+ )
+ }
@Test
- fun `File should be stored under peer subdirectory if key is bound`() = runTest {
- val keystore = MockFilePrivateKeyStore(keystoreRoot)
-
- keystore.saveSessionKey(
- sessionKeypair.privateKey,
- sessionKeypair.sessionKey.keyId,
- privateId,
- peerId,
- )
-
- assertEquals(
- sessionKeypair.privateKey.encoded.asList(),
- readKeyData(boundSessionKeyFilePath).asList()
- )
- }
+ fun `File should be stored under peer subdirectory if key is bound`() =
+ runTest {
+ val keystore = MockFilePrivateKeyStore(keystoreRoot)
+
+ keystore.saveSessionKey(
+ sessionKeypair.privateKey,
+ sessionKeypair.sessionKey.keyId,
+ privateId,
+ peerId,
+ )
+
+ assertEquals(
+ sessionKeypair.privateKey.encoded.asList(),
+ readKeyData(boundSessionKeyFilePath).asList(),
+ )
+ }
@Test
fun `File should not be stored under a peer subdirectory if key is unbound`() =
@@ -233,7 +244,7 @@ class FilePrivateKeyStoreTest : KeystoreTestCase() {
assertEquals(
sessionKeypair.privateKey.encoded.asList(),
- readKeyData(unboundSessionKeyFilePath).asList()
+ readKeyData(unboundSessionKeyFilePath).asList(),
)
}
@@ -241,243 +252,268 @@ class FilePrivateKeyStoreTest : KeystoreTestCase() {
}
@Nested
- inner class RetrieveSession : PrivateKeyStoreRetrievalTestCase(
- keystoreRoot,
- unboundSessionKeyFilePath,
- {
- retrieveSessionKey(
- sessionKeypair.sessionKey.keyId,
- privateId,
- peerId
- )
- }
- ) {
- override fun `Private key should be returned if file exists`() = runTest {
- val keystore = MockFilePrivateKeyStore(keystoreRoot)
- keystore.saveSessionKey(
- sessionKeypair.privateKey,
- sessionKeypair.sessionKey.keyId,
- privateId
- )
-
- val sessionPrivateKey = keystore.retrieveSessionKey(
- sessionKeypair.sessionKey.keyId,
- privateId,
- peerId
- )
-
- assertEquals(sessionKeypair.privateKey, sessionPrivateKey)
- }
+ inner class RetrieveSession :
+ PrivateKeyStoreRetrievalTestCase(
+ keystoreRoot,
+ unboundSessionKeyFilePath,
+ {
+ retrieveSessionKey(
+ sessionKeypair.sessionKey.keyId,
+ privateId,
+ peerId,
+ )
+ },
+ ) {
+ override fun `Private key should be returned if file exists`() =
+ runTest {
+ val keystore = MockFilePrivateKeyStore(keystoreRoot)
+ keystore.saveSessionKey(
+ sessionKeypair.privateKey,
+ sessionKeypair.sessionKey.keyId,
+ privateId,
+ )
+
+ val sessionPrivateKey =
+ keystore.retrieveSessionKey(
+ sessionKeypair.sessionKey.keyId,
+ privateId,
+ peerId,
+ )
+
+ assertEquals(sessionKeypair.privateKey, sessionPrivateKey)
+ }
@Test
- fun `Bound keys should be retrieved`() = runTest {
- val keystore = MockFilePrivateKeyStore(keystoreRoot)
- keystore.saveSessionKey(
- sessionKeypair.privateKey,
- sessionKeypair.sessionKey.keyId,
- privateId,
- peerId
- )
-
- val privateKey = keystore.retrieveSessionKey(
- sessionKeypair.sessionKey.keyId,
- privateId,
- peerId
- )
-
- assertEquals(sessionKeypair.privateKey.encoded.asList(), privateKey.encoded.asList())
- }
+ fun `Bound keys should be retrieved`() =
+ runTest {
+ val keystore = MockFilePrivateKeyStore(keystoreRoot)
+ keystore.saveSessionKey(
+ sessionKeypair.privateKey,
+ sessionKeypair.sessionKey.keyId,
+ privateId,
+ peerId,
+ )
+
+ val privateKey =
+ keystore.retrieveSessionKey(
+ sessionKeypair.sessionKey.keyId,
+ privateId,
+ peerId,
+ )
+
+ assertEquals(
+ sessionKeypair.privateKey.encoded.asList(),
+ privateKey.encoded.asList(),
+ )
+ }
@Test
- fun `Unbound keys should be retrieved`() = runTest {
- val keystore = MockFilePrivateKeyStore(keystoreRoot)
- keystore.saveSessionKey(
- sessionKeypair.privateKey,
- sessionKeypair.sessionKey.keyId,
- privateId,
- )
-
- val privateKey = keystore.retrieveSessionKey(
- sessionKeypair.sessionKey.keyId,
- privateId,
- peerId
- )
-
- assertEquals(sessionKeypair.privateKey.encoded.asList(), privateKey.encoded.asList())
- }
+ fun `Unbound keys should be retrieved`() =
+ runTest {
+ val keystore = MockFilePrivateKeyStore(keystoreRoot)
+ keystore.saveSessionKey(
+ sessionKeypair.privateKey,
+ sessionKeypair.sessionKey.keyId,
+ privateId,
+ )
+
+ val privateKey =
+ keystore.retrieveSessionKey(
+ sessionKeypair.sessionKey.keyId,
+ privateId,
+ peerId,
+ )
+
+ assertEquals(
+ sessionKeypair.privateKey.encoded.asList(),
+ privateKey.encoded.asList(),
+ )
+ }
}
@Nested
inner class DeleteKeys {
@Test
- fun `Node directory should be deleted even if it contains keys`() = runTest {
- val keystore = MockFilePrivateKeyStore(keystoreRoot)
- keystore.saveIdentityKey(privateKey)
- keystore.saveSessionKey(
- sessionKeypair.privateKey,
- sessionKeypair.sessionKey.keyId,
- privateId,
- )
+ fun `Node directory should be deleted even if it contains keys`() =
+ runTest {
+ val keystore = MockFilePrivateKeyStore(keystoreRoot)
+ keystore.saveIdentityKey(privateKey)
+ keystore.saveSessionKey(
+ sessionKeypair.privateKey,
+ sessionKeypair.sessionKey.keyId,
+ privateId,
+ )
- keystore.deleteKeys(privateId)
+ keystore.deleteKeys(privateId)
- assertFalse(nodeDirectoryPath.exists())
- }
+ assertFalse(nodeDirectoryPath.exists())
+ }
@Test
- fun `Other node directories shouldn't be deleted`() = runTest {
- val keystore = MockFilePrivateKeyStore(keystoreRoot)
- keystore.saveIdentityKey(privateKey)
- val node2Directory = nodeDirectoryPath.resolveSibling("node2")
- node2Directory.createDirectories()
- val node3Directory = nodeDirectoryPath.resolveSibling("node3")
- node3Directory.createDirectories()
+ fun `Other node directories shouldn't be deleted`() =
+ runTest {
+ val keystore = MockFilePrivateKeyStore(keystoreRoot)
+ keystore.saveIdentityKey(privateKey)
+ val node2Directory = nodeDirectoryPath.resolveSibling("node2")
+ node2Directory.createDirectories()
+ val node3Directory = nodeDirectoryPath.resolveSibling("node3")
+ node3Directory.createDirectories()
- keystore.deleteKeys(privateId)
+ keystore.deleteKeys(privateId)
- assertTrue(node2Directory.exists())
- assertTrue(node3Directory.exists())
- }
+ assertTrue(node2Directory.exists())
+ assertTrue(node3Directory.exists())
+ }
@Test
- fun `Nothing should happen if the node directory doesn't exist`() = runTest {
- assertFalse(nodeDirectoryPath.exists())
- val keystore = MockFilePrivateKeyStore(keystoreRoot)
+ fun `Nothing should happen if the node directory doesn't exist`() =
+ runTest {
+ assertFalse(nodeDirectoryPath.exists())
+ val keystore = MockFilePrivateKeyStore(keystoreRoot)
- keystore.deleteKeys(privateId)
+ keystore.deleteKeys(privateId)
- assertFalse(nodeDirectoryPath.exists())
- }
+ assertFalse(nodeDirectoryPath.exists())
+ }
@Test
@DisabledOnOs(OS.WINDOWS)
- fun `Exception should be thrown if node directory couldn't be deleted`() = runTest {
- val keystore = MockFilePrivateKeyStore(keystoreRoot)
- keystore.saveIdentityKey(privateKey)
- nodeDirectoryPath.toFile().setWritable(false)
-
- val exception =
- assertThrows { keystore.deleteKeys(privateId) }
-
- assertEquals(
- "Failed to delete node directory for $privateId",
- exception.message
- )
- }
+ fun `Exception should be thrown if node directory couldn't be deleted`() =
+ runTest {
+ val keystore = MockFilePrivateKeyStore(keystoreRoot)
+ keystore.saveIdentityKey(privateKey)
+ nodeDirectoryPath.toFile().setWritable(false)
+
+ val exception =
+ assertThrows { keystore.deleteKeys(privateId) }
+
+ assertEquals(
+ "Failed to delete node directory for $privateId",
+ exception.message,
+ )
+ }
}
@Nested
inner class DeleteBoundSessionKeys {
@Test
- fun `Keys linked to peer should not be deleted from other nodes`() = runTest {
- val keystore = MockFilePrivateKeyStore(keystoreRoot)
- keystore.saveSessionKey(
- sessionKeypair.privateKey,
- sessionKeypair.sessionKey.keyId,
- privateId,
- peerId,
- )
- val node2PrivateAddress = "AnotherPrivateAddress"
- keystore.saveSessionKey(
- sessionKeypair.privateKey,
- sessionKeypair.sessionKey.keyId,
- node2PrivateAddress,
- peerId,
- )
- val boundSessionKey2FilePath = privateKeystoreRootFile.resolve(node2PrivateAddress)
- .resolve("session")
- .resolve(peerId)
- .resolve(byteArrayToHex(sessionKeypair.sessionKey.keyId))
- .toPath()
- assertTrue(boundSessionKey2FilePath.exists())
-
- keystore.deleteBoundSessionKeys(privateId, peerId)
-
- assertFalse(boundSessionKeyFilePath.parent.exists())
- assertTrue(boundSessionKey2FilePath.parent.exists())
- }
+ fun `Keys linked to peer should not be deleted from other nodes`() =
+ runTest {
+ val keystore = MockFilePrivateKeyStore(keystoreRoot)
+ keystore.saveSessionKey(
+ sessionKeypair.privateKey,
+ sessionKeypair.sessionKey.keyId,
+ privateId,
+ peerId,
+ )
+ val node2PrivateAddress = "AnotherPrivateAddress"
+ keystore.saveSessionKey(
+ sessionKeypair.privateKey,
+ sessionKeypair.sessionKey.keyId,
+ node2PrivateAddress,
+ peerId,
+ )
+ val boundSessionKey2FilePath =
+ privateKeystoreRootFile
+ .resolve(node2PrivateAddress)
+ .resolve("session")
+ .resolve(peerId)
+ .resolve(byteArrayToHex(sessionKeypair.sessionKey.keyId))
+ .toPath()
+ assertTrue(boundSessionKey2FilePath.exists())
- @Test
- fun `Keys linked to other peers should not be deleted`() = runTest {
- val keystore = MockFilePrivateKeyStore(keystoreRoot)
- val peer2PrivateAddress = "Peer2Address"
- val peer2SessionKeypair = SessionKeyPair.generate()
- keystore.saveSessionKey(
- peer2SessionKeypair.privateKey,
- peer2SessionKeypair.sessionKey.keyId,
- privateId,
- peer2PrivateAddress,
- )
-
- keystore.deleteBoundSessionKeys(privateId, peerId)
-
- keystore.retrieveSessionKey(
- peer2SessionKeypair.sessionKey.keyId,
- privateId,
- peer2PrivateAddress,
- )
- }
+ keystore.deleteBoundSessionKeys(privateId, peerId)
+
+ assertFalse(boundSessionKeyFilePath.parent.exists())
+ assertTrue(boundSessionKey2FilePath.parent.exists())
+ }
@Test
- fun `Unbound keys should not be deleted`() = runTest {
- val keystore = MockFilePrivateKeyStore(keystoreRoot)
- keystore.saveSessionKey(
- sessionKeypair.privateKey,
- sessionKeypair.sessionKey.keyId,
- privateId,
- peerId,
- )
- val unboundSessionKeypair = SessionKeyPair.generate()
- keystore.saveSessionKey(
- unboundSessionKeypair.privateKey,
- unboundSessionKeypair.sessionKey.keyId,
- privateId,
- )
-
- keystore.deleteBoundSessionKeys(privateId, peerId)
-
- assertThrows {
+ fun `Keys linked to other peers should not be deleted`() =
+ runTest {
+ val keystore = MockFilePrivateKeyStore(keystoreRoot)
+ val peer2PrivateAddress = "Peer2Address"
+ val peer2SessionKeypair = SessionKeyPair.generate()
+ keystore.saveSessionKey(
+ peer2SessionKeypair.privateKey,
+ peer2SessionKeypair.sessionKey.keyId,
+ privateId,
+ peer2PrivateAddress,
+ )
+
+ keystore.deleteBoundSessionKeys(privateId, peerId)
+
keystore.retrieveSessionKey(
+ peer2SessionKeypair.sessionKey.keyId,
+ privateId,
+ peer2PrivateAddress,
+ )
+ }
+
+ @Test
+ fun `Unbound keys should not be deleted`() =
+ runTest {
+ val keystore = MockFilePrivateKeyStore(keystoreRoot)
+ keystore.saveSessionKey(
+ sessionKeypair.privateKey,
sessionKeypair.sessionKey.keyId,
privateId,
peerId,
)
+ val unboundSessionKeypair = SessionKeyPair.generate()
+ keystore.saveSessionKey(
+ unboundSessionKeypair.privateKey,
+ unboundSessionKeypair.sessionKey.keyId,
+ privateId,
+ )
+
+ keystore.deleteBoundSessionKeys(privateId, peerId)
+
+ assertThrows {
+ keystore.retrieveSessionKey(
+ sessionKeypair.sessionKey.keyId,
+ privateId,
+ peerId,
+ )
+ }
+ keystore.retrieveSessionKey(
+ unboundSessionKeypair.sessionKey.keyId,
+ privateId,
+ peerId,
+ )
}
- keystore.retrieveSessionKey(
- unboundSessionKeypair.sessionKey.keyId,
- privateId,
- peerId,
- )
- }
@Test
- fun `Nothing should happen if the root directory doesn't exist`() = runTest {
- val keystore = MockFilePrivateKeyStore(keystoreRoot)
+ fun `Nothing should happen if the root directory doesn't exist`() =
+ runTest {
+ val keystore = MockFilePrivateKeyStore(keystoreRoot)
- keystore.deleteBoundSessionKeys(privateId, peerId)
- }
+ keystore.deleteBoundSessionKeys(privateId, peerId)
+ }
@Test
@DisabledOnOs(OS.WINDOWS)
- fun `Exception should be thrown if a directory couldn't be deleted`() = runTest {
- val keystore = MockFilePrivateKeyStore(keystoreRoot)
- keystore.saveSessionKey(
- sessionKeypair.privateKey,
- sessionKeypair.sessionKey.keyId,
- privateId,
- peerId,
- )
- boundSessionKeyFilePath.parent.toFile().setWritable(false)
-
- val exception = assertThrows {
- keystore.deleteBoundSessionKeys(privateId, peerId)
- }
+ fun `Exception should be thrown if a directory couldn't be deleted`() =
+ runTest {
+ val keystore = MockFilePrivateKeyStore(keystoreRoot)
+ keystore.saveSessionKey(
+ sessionKeypair.privateKey,
+ sessionKeypair.sessionKey.keyId,
+ privateId,
+ peerId,
+ )
+ boundSessionKeyFilePath.parent.toFile().setWritable(false)
+
+ val exception =
+ assertThrows {
+ keystore.deleteBoundSessionKeys(privateId, peerId)
+ }
- assertEquals(
- "Failed to delete session keys for node $privateId and peer $peerId",
- exception.message
- )
- }
+ assertEquals(
+ "Failed to delete session keys for node $privateId and peer $peerId",
+ exception.message,
+ )
+ }
}
private fun byteArrayToHex(byteArray: ByteArray) =
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 fa34837..7c7dac3 100644
--- a/src/test/kotlin/tech/relaycorp/awala/keystores/file/FileSessionPublicKeystoreTest.kt
+++ b/src/test/kotlin/tech/relaycorp/awala/keystores/file/FileSessionPublicKeystoreTest.kt
@@ -41,121 +41,131 @@ class FileSessionPublicKeystoreTest : KeystoreTestCase() {
@Nested
inner class Save {
@Test
- fun `Keystore directory should be reused if it already exists`() = runTest {
- publicKeystoreRootPath.createDirectory()
- val keystore = FileSessionPublicKeystore(keystoreRoot)
+ fun `Keystore directory should be reused if it already exists`() =
+ runTest {
+ publicKeystoreRootPath.createDirectory()
+ val keystore = FileSessionPublicKeystore(keystoreRoot)
- keystore.save(sessionKeyPair.sessionKey, nodeId, peerId)
+ keystore.save(sessionKeyPair.sessionKey, nodeId, peerId)
- readKeyData()
- }
+ readKeyData()
+ }
@Test
- fun `Keystore directory should be created if it doesn't already exist`() = runTest {
- assertFalse(publicKeystoreRootPath.exists())
- val keystore = FileSessionPublicKeystore(keystoreRoot)
+ fun `Keystore directory should be created if it doesn't already exist`() =
+ runTest {
+ assertFalse(publicKeystoreRootPath.exists())
+ val keystore = FileSessionPublicKeystore(keystoreRoot)
- keystore.save(sessionKeyPair.sessionKey, nodeId, peerId)
+ keystore.save(sessionKeyPair.sessionKey, nodeId, peerId)
- readKeyData()
- }
+ readKeyData()
+ }
@Test
- fun `Root directory should be created if it doesn't already exist`() = runTest {
- keystoreRoot.directory.delete()
- val keystore = FileSessionPublicKeystore(keystoreRoot)
+ fun `Root directory should be created if it doesn't already exist`() =
+ runTest {
+ keystoreRoot.directory.delete()
+ val keystore = FileSessionPublicKeystore(keystoreRoot)
- keystore.save(sessionKeyPair.sessionKey, nodeId, peerId)
+ keystore.save(sessionKeyPair.sessionKey, nodeId, peerId)
- readKeyData()
- }
+ readKeyData()
+ }
@Test
@DisabledOnOs(OS.WINDOWS)
- fun `Errors creating parent directory should be wrapped`() = runTest {
- keystoreRoot.directory.setExecutable(false)
- keystoreRoot.directory.setWritable(false)
- val keystore = FileSessionPublicKeystore(keystoreRoot)
+ fun `Errors creating parent directory should be wrapped`() =
+ runTest {
+ keystoreRoot.directory.setExecutable(false)
+ keystoreRoot.directory.setWritable(false)
+ val keystore = FileSessionPublicKeystore(keystoreRoot)
- val exception = assertThrows {
- keystore.save(sessionKeyPair.sessionKey, nodeId, peerId)
- }
+ val exception =
+ assertThrows {
+ keystore.save(sessionKeyPair.sessionKey, nodeId, peerId)
+ }
- assertEquals(
- "Failed to create root directory for public keys",
- exception.message
- )
- }
+ assertEquals(
+ "Failed to create root directory for public keys",
+ exception.message,
+ )
+ }
@Test
- fun `New file should be created if there is no prior key for peer`() = runTest {
- assertFalse(keyDataFilePath.exists())
- val creationTime = ZonedDateTime.now()
- val keystore = FileSessionPublicKeystore(keystoreRoot)
-
- keystore.save(sessionKeyPair.sessionKey, nodeId, peerId, creationTime)
-
- val savedKeyData = readKeyData()
- assertEquals(
- sessionKeyPair.sessionKey.keyId.asList(),
- savedKeyData.readBinaryData("key_id").data.asList()
- )
- assertEquals(
- sessionKeyPair.sessionKey.publicKey.encoded.asList(),
- savedKeyData.readBinaryData("key_der").data.asList()
- )
- assertEquals(
- creationTime.toEpochSecond(),
- savedKeyData.readInt32("creation_timestamp").toLong()
- )
- }
+ fun `New file should be created if there is no prior key for peer`() =
+ runTest {
+ assertFalse(keyDataFilePath.exists())
+ val creationTime = ZonedDateTime.now()
+ val keystore = FileSessionPublicKeystore(keystoreRoot)
- @Test
- 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, nodeId, peerId, now.minusSeconds(1))
- val (newSessionKey) = SessionKeyPair.generate()
-
- keystore.save(newSessionKey, nodeId, peerId, now)
-
- val savedKeyData = readKeyData()
- assertEquals(
- newSessionKey.keyId.asList(),
- savedKeyData.readBinaryData("key_id").data.asList()
- )
- assertEquals(
- newSessionKey.publicKey.encoded.asList(),
- savedKeyData.readBinaryData("key_der").data.asList()
- )
- assertEquals(
- now.toEpochSecond(),
- savedKeyData.readInt32("creation_timestamp").toLong()
- )
- }
+ keystore.save(sessionKeyPair.sessionKey, nodeId, peerId, creationTime)
+
+ val savedKeyData = readKeyData()
+ assertEquals(
+ sessionKeyPair.sessionKey.keyId.asList(),
+ savedKeyData.readBinaryData("key_id").data.asList(),
+ )
+ assertEquals(
+ sessionKeyPair.sessionKey.publicKey.encoded
+ .asList(),
+ savedKeyData.readBinaryData("key_der").data.asList(),
+ )
+ assertEquals(
+ creationTime.toEpochSecond(),
+ savedKeyData.readInt32("creation_timestamp").toLong(),
+ )
+ }
@Test
- fun `Errors creating or updating file should be wrapped`() = runTest {
- val keystore = FileSessionPublicKeystore(keystoreRoot)
- // 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, nodeId, peerId)
+ 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, nodeId, peerId, now.minusSeconds(1))
+ val (newSessionKey) = SessionKeyPair.generate()
+
+ keystore.save(newSessionKey, nodeId, peerId, now)
+
+ val savedKeyData = readKeyData()
+ assertEquals(
+ newSessionKey.keyId.asList(),
+ savedKeyData.readBinaryData("key_id").data.asList(),
+ )
+ assertEquals(
+ newSessionKey.publicKey.encoded.asList(),
+ savedKeyData.readBinaryData("key_der").data.asList(),
+ )
+ assertEquals(
+ now.toEpochSecond(),
+ savedKeyData.readInt32("creation_timestamp").toLong(),
+ )
}
- assertEquals(
- "Failed to save key data to file",
- exception.message
- )
- assertTrue(exception.cause is IOException)
- }
+ @Test
+ fun `Errors creating or updating file should be wrapped`() =
+ runTest {
+ val keystore = FileSessionPublicKeystore(keystoreRoot)
+ // 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, nodeId, peerId)
+ }
+
+ assertEquals(
+ "Failed to save key data to file",
+ exception.message,
+ )
+ assertTrue(exception.cause is IOException)
+ }
private fun readKeyData() =
BsonBinaryReader(ByteBuffer.wrap(keyDataFilePath.toFile().readBytes())).also {
@@ -182,109 +192,128 @@ class FileSessionPublicKeystoreTest : KeystoreTestCase() {
}
@Test
- fun `Key should be reported as missing if the file doesn't exist`() = runTest {
- val keystore = FileSessionPublicKeystore(keystoreRoot)
+ fun `Key should be reported as missing if the file doesn't exist`() =
+ runTest {
+ val keystore = FileSessionPublicKeystore(keystoreRoot)
- assertThrows { keystore.retrieve(nodeId, peerId) }
- }
+ assertThrows { keystore.retrieve(nodeId, peerId) }
+ }
@Test
@DisabledOnOs(OS.WINDOWS) // Windows can't tell apart between not-readable and non-existing
- fun `Exception should be thrown if file isn't readable`() = runTest {
- keyDataFilePath.toFile().createNewFile()
- keyDataFilePath.toFile().setReadable(false)
- val keystore = FileSessionPublicKeystore(keystoreRoot)
+ fun `Exception should be thrown if file isn't readable`() =
+ runTest {
+ keyDataFilePath.toFile().createNewFile()
+ keyDataFilePath.toFile().setReadable(false)
+ val keystore = FileSessionPublicKeystore(keystoreRoot)
- val exception = assertThrows {
- keystore.retrieve(nodeId, peerId)
- }
+ val exception =
+ assertThrows {
+ keystore.retrieve(nodeId, peerId)
+ }
- assertEquals("Failed to read key file", exception.message)
- assertTrue(exception.cause is IOException)
- }
+ assertEquals("Failed to read key file", exception.message)
+ assertTrue(exception.cause is IOException)
+ }
@Test
- fun `Exception should be thrown if file is not BSON-serialized`() = runTest {
- saveKeyData("not BSON".toByteArray())
- val keystore = FileSessionPublicKeystore(keystoreRoot)
+ fun `Exception should be thrown if file is not BSON-serialized`() =
+ runTest {
+ saveKeyData("not BSON".toByteArray())
+ val keystore = FileSessionPublicKeystore(keystoreRoot)
- val exception = assertThrows {
- keystore.retrieve(nodeId, peerId)
- }
+ val exception =
+ assertThrows {
+ keystore.retrieve(nodeId, peerId)
+ }
- assertEquals("Key file is malformed", exception.message)
- assertTrue(exception.cause is BSONException)
- }
+ assertEquals("Key file is malformed", exception.message)
+ assertTrue(exception.cause is BSONException)
+ }
@Test
- fun `Exception should be thrown if key id is missing`() = runTest {
- saveKeyData {
- writeBinaryData("key_der", BsonBinary(sessionKeyPair.sessionKey.publicKey.encoded))
- writeInt32("creation_timestamp", creationTimestamp)
- }
- val keystore = FileSessionPublicKeystore(keystoreRoot)
+ fun `Exception should be thrown if key id is missing`() =
+ runTest {
+ saveKeyData {
+ writeBinaryData(
+ "key_der",
+ BsonBinary(sessionKeyPair.sessionKey.publicKey.encoded),
+ )
+ writeInt32("creation_timestamp", creationTimestamp)
+ }
+ val keystore = FileSessionPublicKeystore(keystoreRoot)
- val exception = assertThrows {
- keystore.retrieve(nodeId, peerId)
- }
+ val exception =
+ assertThrows {
+ keystore.retrieve(nodeId, peerId)
+ }
- assertEquals("Key file is malformed", exception.message)
- assertTrue(exception.cause is BSONException)
- }
+ assertEquals("Key file is malformed", exception.message)
+ assertTrue(exception.cause is BSONException)
+ }
@Test
- fun `Exception should be thrown if public key is missing`() = runTest {
- saveKeyData {
- writeBinaryData("key_id", BsonBinary(sessionKeyPair.sessionKey.keyId))
- writeInt32("creation_timestamp", creationTimestamp)
- }
- val keystore = FileSessionPublicKeystore(keystoreRoot)
+ fun `Exception should be thrown if public key is missing`() =
+ runTest {
+ saveKeyData {
+ writeBinaryData("key_id", BsonBinary(sessionKeyPair.sessionKey.keyId))
+ writeInt32("creation_timestamp", creationTimestamp)
+ }
+ val keystore = FileSessionPublicKeystore(keystoreRoot)
- val exception = assertThrows {
- keystore.retrieve(nodeId, peerId)
- }
+ val exception =
+ assertThrows {
+ keystore.retrieve(nodeId, peerId)
+ }
- assertEquals("Key file is malformed", exception.message)
- assertTrue(exception.cause is BSONException)
- }
+ assertEquals("Key file is malformed", exception.message)
+ assertTrue(exception.cause is BSONException)
+ }
@Test
- fun `Exception should be thrown if creation timestamp is missing`() = runTest {
- saveKeyData {
- writeBinaryData("key_id", BsonBinary(sessionKeyPair.sessionKey.keyId))
- writeBinaryData("key_der", BsonBinary(sessionKeyPair.sessionKey.publicKey.encoded))
- }
- val keystore = FileSessionPublicKeystore(keystoreRoot)
+ fun `Exception should be thrown if creation timestamp is missing`() =
+ runTest {
+ saveKeyData {
+ writeBinaryData("key_id", BsonBinary(sessionKeyPair.sessionKey.keyId))
+ writeBinaryData(
+ "key_der",
+ BsonBinary(sessionKeyPair.sessionKey.publicKey.encoded),
+ )
+ }
+ val keystore = FileSessionPublicKeystore(keystoreRoot)
- val exception = assertThrows {
- keystore.retrieve(nodeId, peerId)
- }
+ val exception =
+ assertThrows {
+ keystore.retrieve(nodeId, peerId)
+ }
- assertEquals("Key file is malformed", exception.message)
- assertTrue(exception.cause is BsonInvalidOperationException)
- }
+ assertEquals("Key file is malformed", exception.message)
+ assertTrue(exception.cause is BsonInvalidOperationException)
+ }
@Test
- fun `Data should be returned if file exists and is valid`() = runTest {
- val keystore = FileSessionPublicKeystore(keystoreRoot)
- keystore.save(sessionKeyPair.sessionKey, nodeId, peerId)
+ fun `Data should be returned if file exists and is valid`() =
+ runTest {
+ val keystore = FileSessionPublicKeystore(keystoreRoot)
+ keystore.save(sessionKeyPair.sessionKey, nodeId, peerId)
- val key = keystore.retrieve(nodeId, peerId)
+ val key = keystore.retrieve(nodeId, peerId)
- assertEquals(sessionKeyPair.sessionKey, key)
- }
+ assertEquals(sessionKeyPair.sessionKey, key)
+ }
private fun saveKeyData(data: ByteArray) = keyDataFilePath.toFile().writeBytes(data)
private fun saveKeyData(writeBsonFields: BsonBinaryWriter.() -> Unit) {
- val bsonSerialization = BasicOutputBuffer().use { buffer ->
- BsonBinaryWriter(buffer).use {
- it.writeStartDocument()
- writeBsonFields(it)
- it.writeEndDocument()
+ val bsonSerialization =
+ BasicOutputBuffer().use { buffer ->
+ BsonBinaryWriter(buffer).use {
+ it.writeStartDocument()
+ writeBsonFields(it)
+ it.writeEndDocument()
+ }
+ buffer.toByteArray()
}
- buffer.toByteArray()
- }
saveKeyData(bsonSerialization)
}
}
@@ -297,28 +326,31 @@ class FileSessionPublicKeystoreTest : KeystoreTestCase() {
}
@Test
- fun `Deletion should be skipped if the root directory doesn't exist`() = runTest {
- publicKeystoreRootPath.deleteExisting()
- val keystore = FileSessionPublicKeystore(keystoreRoot)
+ fun `Deletion should be skipped if the root directory doesn't exist`() =
+ runTest {
+ publicKeystoreRootPath.deleteExisting()
+ val keystore = FileSessionPublicKeystore(keystoreRoot)
- keystore.delete(nodeId, peerId)
- }
+ keystore.delete(nodeId, peerId)
+ }
@Test
- fun `Deletion should be skipped if the file doesn't exist`() = runTest {
- val keystore = FileSessionPublicKeystore(keystoreRoot)
+ fun `Deletion should be skipped if the file doesn't exist`() =
+ runTest {
+ val keystore = FileSessionPublicKeystore(keystoreRoot)
- keystore.delete(nodeId, peerId)
- }
+ keystore.delete(nodeId, peerId)
+ }
@Test
- fun `File should be deleted if it exists`() = runTest {
- val keystore = FileSessionPublicKeystore(keystoreRoot)
- keystore.save(sessionKeyPair.sessionKey, nodeId, peerId)
+ fun `File should be deleted if it exists`() =
+ runTest {
+ val keystore = FileSessionPublicKeystore(keystoreRoot)
+ keystore.save(sessionKeyPair.sessionKey, nodeId, peerId)
- keystore.delete(nodeId, peerId)
+ keystore.delete(nodeId, peerId)
- assertThrows { keystore.retrieve(nodeId, peerId) }
- }
+ assertThrows { keystore.retrieve(nodeId, peerId) }
+ }
}
}
diff --git a/src/test/kotlin/tech/relaycorp/awala/keystores/file/MockFilePrivateKeyStore.kt b/src/test/kotlin/tech/relaycorp/awala/keystores/file/MockFilePrivateKeyStore.kt
index f27915d..f4aae92 100644
--- a/src/test/kotlin/tech/relaycorp/awala/keystores/file/MockFilePrivateKeyStore.kt
+++ b/src/test/kotlin/tech/relaycorp/awala/keystores/file/MockFilePrivateKeyStore.kt
@@ -11,7 +11,9 @@ import kotlin.test.assertEquals
*
* But it doesn't actually encrypt anything.
*/
-class MockFilePrivateKeyStore(keystoreRoot: FileKeystoreRoot) : FilePrivateKeyStore(keystoreRoot) {
+class MockFilePrivateKeyStore(
+ keystoreRoot: FileKeystoreRoot,
+) : FilePrivateKeyStore(keystoreRoot) {
override fun makeEncryptedOutputStream(file: File): OutputStream {
val stream = file.outputStream()
stream.write(header)
@@ -32,7 +34,7 @@ class MockFilePrivateKeyStore(keystoreRoot: FileKeystoreRoot) : FilePrivateKeySt
val fileContents = file.readBytes()
assertEquals(
header.toString(charset),
- fileContents.slice(header.indices).toByteArray().toString(charset)
+ fileContents.slice(header.indices).toByteArray().toString(charset),
)
return fileContents.slice(header.size until fileContents.size).toByteArray()
}
diff --git a/src/test/kotlin/tech/relaycorp/awala/keystores/file/PrivateKeyStoreRetrievalTestCase.kt b/src/test/kotlin/tech/relaycorp/awala/keystores/file/PrivateKeyStoreRetrievalTestCase.kt
index 278115e..25f7adf 100644
--- a/src/test/kotlin/tech/relaycorp/awala/keystores/file/PrivateKeyStoreRetrievalTestCase.kt
+++ b/src/test/kotlin/tech/relaycorp/awala/keystores/file/PrivateKeyStoreRetrievalTestCase.kt
@@ -21,37 +21,40 @@ import tech.relaycorp.relaynet.keystores.MissingKeyException
abstract class PrivateKeyStoreRetrievalTestCase(
private val keystoreRoot: FileKeystoreRoot,
private val keyFilePath: Path,
- private val retrieveMethod: suspend FilePrivateKeyStore.() -> Unit
+ private val retrieveMethod: suspend FilePrivateKeyStore.() -> Unit,
) {
@Test
- fun `Key should be reported as missing if parent directory doesn't exist`() = runTest {
- assertFalse(keyFilePath.parent.exists())
- val keystore = MockFilePrivateKeyStore(keystoreRoot)
+ fun `Key should be reported as missing if parent directory doesn't exist`() =
+ runTest {
+ assertFalse(keyFilePath.parent.exists())
+ val keystore = MockFilePrivateKeyStore(keystoreRoot)
- assertThrows { retrieveMethod(keystore) }
- }
+ assertThrows { retrieveMethod(keystore) }
+ }
@Test
- fun `Key should be reported as missing if the file doesn't exist`() = runTest {
- keyFilePath.parent.createDirectories()
- val keystore = MockFilePrivateKeyStore(keystoreRoot)
+ fun `Key should be reported as missing if the file doesn't exist`() =
+ runTest {
+ keyFilePath.parent.createDirectories()
+ val keystore = MockFilePrivateKeyStore(keystoreRoot)
- assertThrows { retrieveMethod(keystore) }
- }
+ assertThrows { retrieveMethod(keystore) }
+ }
@Test
@DisabledOnOs(OS.WINDOWS) // Windows can't tell apart between not-readable and non-existing
- fun `Exception should be thrown if file isn't readable`() = runTest {
- keyFilePath.parent.createDirectories()
- keyFilePath.createFile()
- keyFilePath.toFile().setReadable(false)
- val keystore = MockFilePrivateKeyStore(keystoreRoot)
-
- val exception = assertThrows { retrieveMethod(keystore) }
-
- assertEquals("Failed to read key file", exception.message)
- assertTrue(exception.cause is IOException)
- }
+ fun `Exception should be thrown if file isn't readable`() =
+ runTest {
+ keyFilePath.parent.createDirectories()
+ keyFilePath.createFile()
+ keyFilePath.toFile().setReadable(false)
+ val keystore = MockFilePrivateKeyStore(keystoreRoot)
+
+ val exception = assertThrows { retrieveMethod(keystore) }
+
+ assertEquals("Failed to read key file", exception.message)
+ assertTrue(exception.cause is IOException)
+ }
abstract fun `Private key should be returned if file exists`()
}
diff --git a/src/test/kotlin/tech/relaycorp/awala/keystores/file/PrivateKeyStoreSavingTestCase.kt b/src/test/kotlin/tech/relaycorp/awala/keystores/file/PrivateKeyStoreSavingTestCase.kt
index 362fbd7..2ab0ea4 100644
--- a/src/test/kotlin/tech/relaycorp/awala/keystores/file/PrivateKeyStoreSavingTestCase.kt
+++ b/src/test/kotlin/tech/relaycorp/awala/keystores/file/PrivateKeyStoreSavingTestCase.kt
@@ -19,80 +19,88 @@ import org.junit.jupiter.api.condition.OS
abstract class PrivateKeyStoreSavingTestCase(
private val keystoreRoot: FileKeystoreRoot,
private val keyFilePath: Path,
- private val saveMethod: suspend FilePrivateKeyStore.() -> Unit
+ private val saveMethod: suspend FilePrivateKeyStore.() -> Unit,
) {
@Test
- fun `Parent subdirectory should be reused if it exists`() = runTest {
- keyFilePath.parent.createDirectories()
- val keystore = MockFilePrivateKeyStore(keystoreRoot)
+ fun `Parent subdirectory should be reused if it exists`() =
+ runTest {
+ keyFilePath.parent.createDirectories()
+ val keystore = MockFilePrivateKeyStore(keystoreRoot)
- saveMethod(keystore)
+ saveMethod(keystore)
- assertTrue(keyFilePath.exists())
- }
+ assertTrue(keyFilePath.exists())
+ }
@Test
- fun `Parent directory should be created if it doesn't exist`() = runTest {
- assertFalse(keyFilePath.parent.exists())
- val keystore = MockFilePrivateKeyStore(keystoreRoot)
+ fun `Parent directory should be created if it doesn't exist`() =
+ runTest {
+ assertFalse(keyFilePath.parent.exists())
+ val keystore = MockFilePrivateKeyStore(keystoreRoot)
- saveMethod(keystore)
+ saveMethod(keystore)
- assertTrue(keyFilePath.exists())
- }
+ assertTrue(keyFilePath.exists())
+ }
@Test
- fun `Root directory should be created if it doesn't exist`() = runTest {
- keystoreRoot.directory.delete()
- val keystore = MockFilePrivateKeyStore(keystoreRoot)
+ fun `Root directory should be created if it doesn't exist`() =
+ runTest {
+ keystoreRoot.directory.delete()
+ val keystore = MockFilePrivateKeyStore(keystoreRoot)
- saveMethod(keystore)
+ saveMethod(keystore)
- assertTrue(keyFilePath.exists())
- }
+ assertTrue(keyFilePath.exists())
+ }
@Test
@DisabledOnOs(OS.WINDOWS)
- fun `Errors creating node subdirectory should be wrapped`() = runTest {
- keystoreRoot.directory.setExecutable(false)
- keystoreRoot.directory.setWritable(false)
- val keystore = MockFilePrivateKeyStore(keystoreRoot)
-
- val exception = assertThrows {
- saveMethod(keystore)
+ fun `Errors creating node subdirectory should be wrapped`() =
+ runTest {
+ keystoreRoot.directory.setExecutable(false)
+ keystoreRoot.directory.setWritable(false)
+ val keystore = MockFilePrivateKeyStore(keystoreRoot)
+
+ val exception =
+ assertThrows {
+ saveMethod(keystore)
+ }
+
+ assertEquals(
+ "Failed to create root directory for private keys",
+ exception.message,
+ )
}
- assertEquals(
- "Failed to create root directory for private keys",
- exception.message
- )
- }
-
@Test
@DisabledOnOs(OS.WINDOWS)
- fun `Errors creating or updating file should be wrapped`() = runTest {
- keyFilePath.parent.createDirectories()
- keyFilePath.toFile().createNewFile()
- keyFilePath.toFile().setWritable(false)
- val keystore = MockFilePrivateKeyStore(keystoreRoot)
-
- val exception = assertThrows {
- saveMethod(keystore)
+ fun `Errors creating or updating file should be wrapped`() =
+ runTest {
+ keyFilePath.parent.createDirectories()
+ keyFilePath.toFile().createNewFile()
+ keyFilePath.toFile().setWritable(false)
+ val keystore = MockFilePrivateKeyStore(keystoreRoot)
+
+ val exception =
+ assertThrows {
+ saveMethod(keystore)
+ }
+
+ assertEquals("Failed to save key file", exception.message)
+ assertTrue(exception.cause is IOException)
}
- assertEquals("Failed to save key file", exception.message)
- assertTrue(exception.cause is IOException)
- }
-
@Test
- fun `New file should be created if key is new`() = runTest {
- assertFalse(keyFilePath.exists())
- val keystore = MockFilePrivateKeyStore(keystoreRoot)
+ fun `New file should be created if key is new`() =
+ runTest {
+ assertFalse(keyFilePath.exists())
+ val keystore = MockFilePrivateKeyStore(keystoreRoot)
- saveMethod(keystore)
+ saveMethod(keystore)
- assertTrue(keyFilePath.exists())
- }
+ assertTrue(keyFilePath.exists())
+ }
@Test
abstract fun `Private key should be stored`()