Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
wip(crypto-pgpainless): add
PGPKeyPair
and PGPKeyManager
Signed-off-by: Aditya Wasan <adityawasan55@gmail.com> (cherry picked from commit 02d07e9)
- Loading branch information
1 parent
5cf2260
commit 7657259
Showing
7 changed files
with
228 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
106 changes: 106 additions & 0 deletions
106
crypto-pgpainless/src/main/kotlin/dev/msfjarvis/aps/crypto/PGPKeyManager.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
/* | ||
* Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved. | ||
* SPDX-License-Identifier: GPL-3.0-only | ||
*/ | ||
|
||
package dev.msfjarvis.aps.crypto | ||
|
||
import androidx.annotation.VisibleForTesting | ||
import com.github.michaelbull.result.Result | ||
import com.github.michaelbull.result.runCatching | ||
import java.io.File | ||
import kotlinx.coroutines.CoroutineDispatcher | ||
import kotlinx.coroutines.withContext | ||
import org.pgpainless.PGPainless | ||
|
||
public class PGPKeyManager( | ||
filesDir: String, | ||
private val dispatcher: CoroutineDispatcher, | ||
) : KeyManager<PGPKeyPair> { | ||
|
||
private val keyDir = File(filesDir, KEY_DIR_NAME) | ||
|
||
override suspend fun addKey(key: PGPKeyPair, replace: Boolean): Result<PGPKeyPair, Throwable> = | ||
withContext(dispatcher) { | ||
runCatching { | ||
if (!keyDirExists()) throw KeyManagerException.KeyDirectoryUnavailableException | ||
val keyFile = File(keyDir, "${key.getKeyId()}.$KEY_EXTENSION") | ||
if (keyFile.exists()) { | ||
// Check for replace flag first and if it is false, throw an error | ||
if (!replace) throw KeyManagerException.KeyAlreadyExistsException(key.getKeyId()) | ||
if (!keyFile.delete()) throw KeyManagerException.KeyDeletionFailedException | ||
} | ||
|
||
keyFile.writeBytes(key.getPrivateKey()) | ||
|
||
key | ||
} | ||
} | ||
|
||
override suspend fun removeKey(key: PGPKeyPair): Result<PGPKeyPair, Throwable> = | ||
withContext(dispatcher) { | ||
runCatching { | ||
if (!keyDirExists()) throw KeyManagerException.KeyDirectoryUnavailableException | ||
val keyFile = File(keyDir, "${key.getKeyId()}.$KEY_EXTENSION") | ||
if (keyFile.exists()) { | ||
if (!keyFile.delete()) throw KeyManagerException.KeyDeletionFailedException | ||
} | ||
|
||
key | ||
} | ||
} | ||
|
||
override suspend fun getKeyById(id: String): Result<PGPKeyPair, Throwable> = | ||
withContext(dispatcher) { | ||
runCatching { | ||
if (!keyDirExists()) throw KeyManagerException.KeyDirectoryUnavailableException | ||
val keys = keyDir.listFiles() | ||
if (keys.isNullOrEmpty()) throw KeyManagerException.NoKeysAvailableException | ||
|
||
for (keyFile in keys) { | ||
val secretKeyRing = PGPainless.readKeyRing().secretKeyRing(keyFile.inputStream()) | ||
val secretKey = secretKeyRing.secretKey | ||
val keyPair = PGPKeyPair(secretKey) | ||
if (keyPair.getKeyId() == id) return@runCatching keyPair | ||
} | ||
|
||
throw KeyManagerException.KeyNotFoundException(id) | ||
} | ||
} | ||
|
||
override suspend fun getAllKeys(): Result<List<PGPKeyPair>, Throwable> = | ||
withContext(dispatcher) { | ||
runCatching { | ||
if (!keyDirExists()) throw KeyManagerException.KeyDirectoryUnavailableException | ||
val keys = keyDir.listFiles() | ||
if (keys.isNullOrEmpty()) return@runCatching listOf() | ||
|
||
keys | ||
.map { keyFile -> | ||
val secretKeyRing = PGPainless.readKeyRing().secretKeyRing(keyFile.inputStream()) | ||
val secretKey = secretKeyRing.secretKey | ||
|
||
PGPKeyPair(secretKey) | ||
} | ||
.toList() | ||
} | ||
} | ||
|
||
override fun canHandle(fileName: String): Boolean { | ||
// TODO: This is a temp hack for now and in future it should check that the GPGKeyManager can | ||
// decrypt the file | ||
return fileName.endsWith(KEY_EXTENSION) | ||
} | ||
|
||
private fun keyDirExists(): Boolean { | ||
return keyDir.exists() || keyDir.mkdirs() | ||
} | ||
|
||
internal companion object { | ||
|
||
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) | ||
internal const val KEY_DIR_NAME: String = "keys" | ||
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) | ||
internal const val KEY_EXTENSION: String = "key" | ||
} | ||
} |
25 changes: 25 additions & 0 deletions
25
crypto-pgpainless/src/main/kotlin/dev/msfjarvis/aps/crypto/PGPKeyPair.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
/* | ||
* Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved. | ||
* SPDX-License-Identifier: GPL-3.0-only | ||
*/ | ||
|
||
package dev.msfjarvis.aps.crypto | ||
|
||
import org.bouncycastle.openpgp.PGPSecretKey | ||
|
||
public class PGPKeyPair(private val secretKey: PGPSecretKey) : KeyPair { | ||
|
||
init { | ||
if (secretKey.isPrivateKeyEmpty) throw KeyPairException.PrivateKeyUnavailableException | ||
} | ||
|
||
override fun getPrivateKey(): ByteArray { | ||
return secretKey.encoded | ||
} | ||
override fun getPublicKey(): ByteArray { | ||
return secretKey.publicKey.encoded | ||
} | ||
override fun getKeyId(): String { | ||
return secretKey.keyID.toString() | ||
} | ||
} |
14 changes: 14 additions & 0 deletions
14
crypto-pgpainless/src/main/kotlin/dev/msfjarvis/aps/crypto/utils/CryptoConstants.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
/* | ||
* Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved. | ||
* SPDX-License-Identifier: GPL-3.0-only | ||
*/ | ||
|
||
package dev.msfjarvis.aps.crypto.utils | ||
|
||
internal object CryptoConstants { | ||
internal const val KEY_PASSPHRASE = "hunter2" | ||
internal const val PLAIN_TEXT = "encryption worthy content" | ||
internal const val KEY_NAME = "John Doe" | ||
internal const val KEY_EMAIL = "john.doe@example.com" | ||
internal const val KEY_ID = "04ace699d5b15b7e" | ||
} |
44 changes: 44 additions & 0 deletions
44
crypto-pgpainless/src/test/kotlin/dev/msfjarvis/aps/crypto/PGPKeyPairTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
/* | ||
* Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved. | ||
* SPDX-License-Identifier: GPL-3.0-only | ||
*/ | ||
|
||
package dev.msfjarvis.aps.crypto | ||
|
||
import dev.msfjarvis.aps.crypto.utils.CryptoConstants | ||
import kotlin.test.assertEquals | ||
import kotlin.test.assertFailsWith | ||
import org.junit.Test | ||
import org.pgpainless.PGPainless | ||
|
||
public class GPGKeyPairTest { | ||
|
||
@Test | ||
public fun testIfKeyIdIsCorrect() { | ||
val secretKey = PGPainless.readKeyRing().secretKeyRing(getKey()).secretKey | ||
val keyPair = PGPKeyPair(secretKey) | ||
|
||
assertEquals(CryptoConstants.KEY_ID, keyPair.getKeyId()) | ||
} | ||
|
||
@Test | ||
public fun testBuildingKeyPairWithoutPrivateKey() { | ||
assertFailsWith<KeyPairException.PrivateKeyUnavailableException> { | ||
// Get public key object from private key | ||
val publicKey = PGPainless.readKeyRing().secretKeyRing(getKey()).publicKey | ||
|
||
// Create secret key ring from public key | ||
val secretKeyRing = PGPainless.readKeyRing().secretKeyRing(publicKey.encoded) | ||
|
||
// Get secret key from key ring | ||
val publicSecretKey = secretKeyRing.secretKey | ||
|
||
// Try creating a KeyPair from public key | ||
val keyPair = PGPKeyPair(publicSecretKey) | ||
|
||
keyPair.getPrivateKey() | ||
} | ||
} | ||
|
||
private fun getKey(): String = this::class.java.classLoader.getResource("private_key").readText() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
-----BEGIN PGP PRIVATE KEY BLOCK----- | ||
Version: GopenPGP 2.1.9 | ||
Comment: https://gopenpgp.org | ||
|
||
xYYEYN+EThYJKwYBBAHaRw8BAQdAh0d9GdVyJV6KbFynPz3sHkdi5RDnKYs+l0x0 | ||
rEOEthX+CQMIfg7BTvTTe7pgvNERA1vLXRjSxXyi7tfSV13JRnrapp7YtNUSHLVS | ||
PqbaLBd6+EXx7dJ9mUSUSWVga5mdtLZ/k6e+6dsygeHiJuwxfGbHnc0fSm9obiBE | ||
b2UgPGpvaG4uZG9lQGV4YW1wbGUuY29tPsKIBBMWCAA6BQJg34ROCRAErOaZ1bFb | ||
fhYhBJQ0DPsSHC5XfslyQwSs5pnVsVt+AhsDAh4BAhkBAwsJBwIVCAIiAQAAtgwB | ||
AOa3rnipQPsxgxvOP1V+2kD6ssiwt6BZRWwPcyfeX1h4AP9ozBYr+PSmNbam9bnq | ||
wgXwuQhPJeWTSgILMaiasugGCMeLBGDfhE4SCisGAQQBl1UBBQEBB0ClFQJX/L2G | ||
EX9ucC5mvwj3X/7aDXDFAmIpQeWYSS1negMBCgn+CQMIF1uko+Ym3thgoDWUgM5e | ||
MNmDG3rYkTa7h6mlhhrsYtE/GN78EJHP1ygFzOczU/YdbxSRTZCu697uPCZLWURV | ||
1+b66KLTMNHNaAkoFb2JC8J4BBgWCAAqBQJg34ROCRAErOaZ1bFbfhYhBJQ0DPsS | ||
HC5XfslyQwSs5pnVsVt+AhsMAAB1CgEApNcEivCSp0f8CnV4UCoSRRRekIbP1Ub2 | ||
GJx6iRJR8xwA/jicDxdnl/Umfd3mWjGk04R47whiDOXdwjBmC1KVBaMH | ||
=Sfsa | ||
-----END PGP PRIVATE KEY BLOCK----- |