summaryrefslogtreecommitdiff
path: root/crypto-pgpainless
diff options
context:
space:
mode:
Diffstat (limited to 'crypto-pgpainless')
-rw-r--r--crypto-pgpainless/src/main/kotlin/app/passwordstore/crypto/PGPKeyManager.kt20
-rw-r--r--crypto-pgpainless/src/test/kotlin/app/passwordstore/crypto/PGPKeyManagerTest.kt76
2 files changed, 77 insertions, 19 deletions
diff --git a/crypto-pgpainless/src/main/kotlin/app/passwordstore/crypto/PGPKeyManager.kt b/crypto-pgpainless/src/main/kotlin/app/passwordstore/crypto/PGPKeyManager.kt
index a80b5dcb..b834164d 100644
--- a/crypto-pgpainless/src/main/kotlin/app/passwordstore/crypto/PGPKeyManager.kt
+++ b/crypto-pgpainless/src/main/kotlin/app/passwordstore/crypto/PGPKeyManager.kt
@@ -21,6 +21,9 @@ import java.io.File
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext
+import org.bouncycastle.openpgp.PGPPublicKeyRing
+import org.bouncycastle.openpgp.PGPSecretKeyRing
+import org.pgpainless.PGPainless
import org.pgpainless.util.selection.userid.SelectUserId
public class PGPKeyManager
@@ -36,9 +39,24 @@ constructor(
withContext(dispatcher) {
runSuspendCatching {
if (!keyDirExists()) throw KeyDirectoryUnavailableException
- if (tryParseKeyring(key) == null) throw InvalidKeyException
+ val incomingKeyRing = tryParseKeyring(key) ?: throw InvalidKeyException
val keyFile = File(keyDir, "${tryGetId(key)}.$KEY_EXTENSION")
if (keyFile.exists()) {
+ val existingKeyBytes = keyFile.readBytes()
+ val existingKeyRing =
+ tryParseKeyring(PGPKey(existingKeyBytes)) ?: throw InvalidKeyException
+ when {
+ existingKeyRing is PGPPublicKeyRing && incomingKeyRing is PGPSecretKeyRing -> {
+ keyFile.writeBytes(key.contents)
+ return@runSuspendCatching key
+ }
+ existingKeyRing is PGPPublicKeyRing && incomingKeyRing is PGPPublicKeyRing -> {
+ val updatedPublicKey = PGPainless.mergeCertificate(existingKeyRing, incomingKeyRing)
+ val keyBytes = PGPainless.asciiArmor(updatedPublicKey).encodeToByteArray()
+ keyFile.writeBytes(keyBytes)
+ return@runSuspendCatching key
+ }
+ }
// Check for replace flag first and if it is false, throw an error
if (!replace)
throw KeyAlreadyExistsException(
diff --git a/crypto-pgpainless/src/test/kotlin/app/passwordstore/crypto/PGPKeyManagerTest.kt b/crypto-pgpainless/src/test/kotlin/app/passwordstore/crypto/PGPKeyManagerTest.kt
index c446cee4..91a9dc47 100644
--- a/crypto-pgpainless/src/test/kotlin/app/passwordstore/crypto/PGPKeyManagerTest.kt
+++ b/crypto-pgpainless/src/test/kotlin/app/passwordstore/crypto/PGPKeyManagerTest.kt
@@ -6,6 +6,8 @@ import app.passwordstore.crypto.TestUtils.getArmoredPrivateKeyWithMultipleIdenti
import app.passwordstore.crypto.errors.KeyAlreadyExistsException
import app.passwordstore.crypto.errors.KeyNotFoundException
import app.passwordstore.crypto.errors.NoKeysAvailableException
+import com.github.michaelbull.result.Err
+import com.github.michaelbull.result.Ok
import com.github.michaelbull.result.unwrap
import com.github.michaelbull.result.unwrapError
import java.io.File
@@ -35,7 +37,8 @@ class PGPKeyManagerTest {
private val dispatcher = StandardTestDispatcher()
private val scope = TestScope(dispatcher)
private val keyManager by unsafeLazy { PGPKeyManager(filesDir.absolutePath, dispatcher) }
- private val key = PGPKey(TestUtils.getArmoredPrivateKey())
+ private val privateKey = PGPKey(TestUtils.getArmoredPrivateKey())
+ private val publicKey = PGPKey(TestUtils.getArmoredPublicKey())
private fun <T> unsafeLazy(initializer: () -> T) =
lazy(LazyThreadSafetyMode.NONE) { initializer.invoke() }
@@ -54,7 +57,7 @@ class PGPKeyManagerTest {
fun addKey() =
scope.runTest {
// Check if the key id returned is correct
- val keyId = keyManager.getKeyId(keyManager.addKey(key).unwrap())
+ val keyId = keyManager.getKeyId(keyManager.addKey(privateKey).unwrap())
assertEquals(KeyId(CryptoConstants.KEY_ID), keyId)
// Check if the keys directory have one file
@@ -69,8 +72,8 @@ class PGPKeyManagerTest {
fun addKeyWithoutReplaceFlag() =
scope.runTest {
// Check adding the keys twice
- keyManager.addKey(key, false).unwrap()
- val error = keyManager.addKey(key, false).unwrapError()
+ keyManager.addKey(privateKey, false).unwrap()
+ val error = keyManager.addKey(privateKey, false).unwrapError()
assertIs<KeyAlreadyExistsException>(error)
}
@@ -79,8 +82,8 @@ class PGPKeyManagerTest {
fun addKeyWithReplaceFlag() =
scope.runTest {
// Check adding the keys twice
- keyManager.addKey(key, true).unwrap()
- val keyId = keyManager.getKeyId(keyManager.addKey(key, true).unwrap())
+ keyManager.addKey(privateKey, true).unwrap()
+ val keyId = keyManager.getKeyId(keyManager.addKey(privateKey, true).unwrap())
assertEquals(KeyId(CryptoConstants.KEY_ID), keyId)
}
@@ -89,10 +92,10 @@ class PGPKeyManagerTest {
fun removeKey() =
scope.runTest {
// Add key using KeyManager
- keyManager.addKey(key).unwrap()
+ keyManager.addKey(privateKey).unwrap()
// Check if the key id returned is correct
- val keyId = keyManager.getKeyId(keyManager.removeKey(key).unwrap())
+ val keyId = keyManager.getKeyId(keyManager.removeKey(privateKey).unwrap())
assertEquals(KeyId(CryptoConstants.KEY_ID), keyId)
// Check if the keys directory have 0 files
@@ -104,42 +107,42 @@ class PGPKeyManagerTest {
fun getKeyById() =
scope.runTest {
// Add key using KeyManager
- keyManager.addKey(key).unwrap()
+ keyManager.addKey(privateKey).unwrap()
- val keyId = keyManager.getKeyId(key)
+ val keyId = keyManager.getKeyId(privateKey)
assertNotNull(keyId)
- assertEquals(KeyId(CryptoConstants.KEY_ID), keyManager.getKeyId(key))
+ assertEquals(KeyId(CryptoConstants.KEY_ID), keyManager.getKeyId(privateKey))
// Check returned key id matches the expected id and the created key id
val returnedKey = keyManager.getKeyById(keyId).unwrap()
- assertEquals(keyManager.getKeyId(key), keyManager.getKeyId(returnedKey))
+ assertEquals(keyManager.getKeyId(privateKey), keyManager.getKeyId(returnedKey))
}
@Test
fun getKeyByFullUserId() =
scope.runTest {
- keyManager.addKey(key).unwrap()
+ keyManager.addKey(privateKey).unwrap()
val keyId = "${CryptoConstants.KEY_NAME} <${CryptoConstants.KEY_EMAIL}>"
val returnedKey = keyManager.getKeyById(UserId(keyId)).unwrap()
- assertEquals(keyManager.getKeyId(key), keyManager.getKeyId(returnedKey))
+ assertEquals(keyManager.getKeyId(privateKey), keyManager.getKeyId(returnedKey))
}
@Test
fun getKeyByEmailUserId() =
scope.runTest {
- keyManager.addKey(key).unwrap()
+ keyManager.addKey(privateKey).unwrap()
val keyId = CryptoConstants.KEY_EMAIL
val returnedKey = keyManager.getKeyById(UserId(keyId)).unwrap()
- assertEquals(keyManager.getKeyId(key), keyManager.getKeyId(returnedKey))
+ assertEquals(keyManager.getKeyId(privateKey), keyManager.getKeyId(returnedKey))
}
@Test
fun getNonExistentKey() =
scope.runTest {
// Add key using KeyManager
- keyManager.addKey(key).unwrap()
+ keyManager.addKey(privateKey).unwrap()
val keyId = KeyId(0x08edf7567183ce44)
@@ -166,7 +169,7 @@ class PGPKeyManagerTest {
assertEquals(0, noKeyList.size)
// Add key using KeyManager
- keyManager.addKey(key).unwrap()
+ keyManager.addKey(privateKey).unwrap()
keyManager.addKey(PGPKey(getArmoredPrivateKeyWithMultipleIdentities())).unwrap()
// Check if KeyManager returns one key
@@ -186,4 +189,41 @@ class PGPKeyManagerTest {
assertContentEquals(johnKey.contents, janeKey.contents)
}
}
+
+ @Test
+ fun replacePrivateKeyWithPublicKey() {
+ scope.runTest {
+ assertIs<Ok<PGPKey>>(keyManager.addKey(privateKey))
+ assertIs<Err<KeyAlreadyExistsException>>(keyManager.addKey(publicKey))
+ }
+ }
+
+ @Test
+ fun replacePublicKeyWithSecretKey() {
+ scope.runTest {
+ assertIs<Ok<PGPKey>>(keyManager.addKey(publicKey))
+ assertIs<Ok<PGPKey>>(keyManager.addKey(privateKey))
+ }
+ }
+
+ @Test
+ fun replacePublicKeyWithPublicKey() {
+ scope.runTest {
+ assertIs<Ok<PGPKey>>(keyManager.addKey(publicKey))
+ assertIs<Ok<PGPKey>>(keyManager.addKey(publicKey))
+ val allKeys = keyManager.getAllKeys()
+ assertIs<Ok<List<PGPKey>>>(allKeys)
+ assertEquals(1, allKeys.value.size)
+ val key = allKeys.value[0]
+ assertContentEquals(publicKey.contents, key.contents)
+ }
+ }
+
+ @Test
+ fun replaceSecretKeyWithSecretKey() {
+ scope.runTest {
+ assertIs<Ok<PGPKey>>(keyManager.addKey(privateKey))
+ assertIs<Err<KeyAlreadyExistsException>>(keyManager.addKey(privateKey))
+ }
+ }
}