Merge branch 'feat/crypto/improvement' into 'master'

Improve KeyHolderCrypto's extension functions

See merge request proton/mobile/android/proton-libs!440
This commit is contained in:
Neil Marietta 2021-10-28 15:56:11 +00:00
commit 946e1d2a3d
8 changed files with 561 additions and 173 deletions

View File

@ -65,10 +65,10 @@ import com.proton.gopenpgp.crypto.SessionKey as InternalSessionKey
/**
* [PGPCrypto] implementation based on GOpenPGP Android library.
*/
@Suppress("TooManyFunctions")
@Suppress("LargeClass", "TooManyFunctions", "MagicNumber")
class GOpenPGPCrypto : PGPCrypto {
// region Private
// region Private Closable
private class CloseableUnlockedKey(val value: Key) : Closeable {
override fun close() {
@ -90,6 +90,10 @@ class GOpenPGPCrypto : PGPCrypto {
}
}
// endregion
// region Private Key
private fun SessionKey.toInternalSessionKey() = InternalSessionKey(key, Constants.AES256)
private fun newKey(key: Key) = CloseableUnlockedKey(key)
@ -108,13 +112,21 @@ class GOpenPGPCrypto : PGPCrypto {
private fun newKeyRing(keys: List<Armored>) =
Crypto.newKeyRing(null).apply { keys.map { Crypto.newKeyFromArmored(it) }.forEach { addKey(it) } }
// endregion
// region Private VerificationTime
private fun VerificationTime.toUtcSeconds(): Long = when (this) {
is VerificationTime.Ignore -> 0
is VerificationTime.Now -> Crypto.getUnixTime() // Value updated by updateTime.
is VerificationTime.Utc -> seconds
}
private fun encrypt(
// endregion
// region Private Encrypt
private fun encryptMessage(
plainMessage: PlainMessage,
publicKey: Armored,
signKeyRing: KeyRing? = null
@ -123,26 +135,21 @@ class GOpenPGPCrypto : PGPCrypto {
return publicKeyRing.encrypt(plainMessage, signKeyRing).armored
}
private fun encrypt(
private fun encryptMessageSessionKey(
plainMessage: PlainMessage,
sessionKey: SessionKey
sessionKey: SessionKey,
signKeyRing: KeyRing? = null,
): DataPacket {
return sessionKey.toInternalSessionKey().encrypt(plainMessage)
}
private fun encryptAndSign(
plainMessage: PlainMessage,
publicKey: Armored,
unlockedKey: Unarmored
): EncryptedMessage {
newKey(unlockedKey).use { key ->
newKeyRing(key).use { keyRing ->
return encrypt(plainMessage, publicKey, keyRing.value)
return sessionKey.toInternalSessionKey().let { internalSessionKey ->
if (signKeyRing != null) {
internalSessionKey.encryptAndSign(plainMessage, signKeyRing)
} else {
internalSessionKey.encrypt(plainMessage)
}
}
}
private fun encrypt(
private fun encryptFileSessionKey(
source: File,
destination: File,
sessionKey: SessionKey,
@ -161,7 +168,7 @@ class GOpenPGPCrypto : PGPCrypto {
}
}
private fun encryptAndSign(
private fun encryptAndSignFileSessionKey(
source: File,
destination: File,
sessionKey: SessionKey,
@ -169,12 +176,69 @@ class GOpenPGPCrypto : PGPCrypto {
): EncryptedFile {
newKey(unlockedKey).use { key ->
newKeyRing(key).use { keyRing ->
return encrypt(source, destination, sessionKey, keyRing.value)
return encryptFileSessionKey(source, destination, sessionKey, keyRing.value)
}
}
}
private fun decrypt(
private fun encryptAndSignMessage(
plainMessage: PlainMessage,
publicKey: Armored,
unlockedKey: Unarmored
): EncryptedMessage {
newKey(unlockedKey).use { key ->
newKeyRing(key).use { keyRing ->
return encryptMessage(plainMessage, publicKey, keyRing.value)
}
}
}
private fun encryptAndSignMessageSessionKey(
plainMessage: PlainMessage,
sessionKey: SessionKey,
unlockedKey: Unarmored,
): DataPacket {
newKey(unlockedKey).use { key ->
newKeyRing(key).use { keyRing ->
return encryptMessageSessionKey(plainMessage, sessionKey, keyRing.value)
}
}
}
// endregion
// region Private Decrypt
private inline fun <T> decryptMessage(
message: EncryptedMessage,
unlockedKey: Unarmored,
block: (PlainMessage) -> T
): T {
val pgpMessage = Crypto.newPGPMessageFromArmored(message)
return decryptMessage(pgpMessage, unlockedKey, block)
}
private inline fun <T> decryptMessage(
pgpMessage: PGPMessage,
unlockedKey: Unarmored,
block: (PlainMessage) -> T
): T {
newKey(unlockedKey).use { key ->
newKeyRing(key).use { keyRing ->
return block(keyRing.value.decrypt(pgpMessage, null, 0))
}
}
}
private fun decryptDataSessionKey(
data: DataPacket,
sessionKey: SessionKey,
): PlainMessage {
val internalSessionKey = sessionKey.toInternalSessionKey()
return internalSessionKey.decrypt(data)
}
private fun decryptFileSessionKey(
source: File,
destination: File,
sessionKey: SessionKey,
@ -199,45 +263,7 @@ class GOpenPGPCrypto : PGPCrypto {
}
}
private fun decrypt(
data: DataPacket,
sessionKey: SessionKey,
): PlainMessage {
return sessionKey.toInternalSessionKey().decrypt(data)
}
private fun decryptAndVerify(
source: File,
destination: File,
sessionKey: SessionKey,
publicKeys: List<Armored>,
validAtUtc: Long
): DecryptedFile {
return decrypt(source, destination, sessionKey, newKeyRing(publicKeys), validAtUtc)
}
private inline fun <T> decrypt(
message: EncryptedMessage,
unlockedKey: Unarmored,
block: (PlainMessage) -> T
): T {
val pgpMessage = Crypto.newPGPMessageFromArmored(message)
return decrypt(pgpMessage, unlockedKey, block)
}
private inline fun <T> decrypt(
pgpMessage: PGPMessage,
unlockedKey: Unarmored,
block: (PlainMessage) -> T
): T {
newKey(unlockedKey).use { key ->
newKeyRing(key).use { keyRing ->
return block(keyRing.value.decrypt(pgpMessage, null, 0))
}
}
}
private inline fun <T> decryptAndVerify(
private inline fun <T> decryptAndVerifyMessage(
msg: EncryptedMessage,
publicKeys: List<Armored>,
unlockedKeys: List<Unarmored>,
@ -253,7 +279,31 @@ class GOpenPGPCrypto : PGPCrypto {
}
}
private fun sign(
private fun decryptAndVerifyDataSessionKey(
data: DataPacket,
sessionKey: SessionKey,
publicKeys: List<Armored>,
validAtUtc: Long = 0
): ExplicitVerifyMessage {
val internalSessionKey = sessionKey.toInternalSessionKey()
return Helper.decryptSessionKeyExplicitVerify(data, internalSessionKey, newKeyRing(publicKeys), validAtUtc)
}
private fun decryptAndVerifyFileSessionKey(
source: File,
destination: File,
sessionKey: SessionKey,
publicKeys: List<Armored>,
validAtUtc: Long
): DecryptedFile {
return decryptFileSessionKey(source, destination, sessionKey, newKeyRing(publicKeys), validAtUtc)
}
// endregion
// region Private Sign
private fun signMessageDetached(
plainMessage: PlainMessage,
unlockedKey: Unarmored
): Signature {
@ -264,7 +314,7 @@ class GOpenPGPCrypto : PGPCrypto {
}
}
private fun sign(
private fun signFileDetached(
source: File,
unlockedKey: Unarmored
): Signature {
@ -278,7 +328,7 @@ class GOpenPGPCrypto : PGPCrypto {
}
}
private fun signEncrypted(
private fun signMessageDetachedEncrypted(
plainMessage: PlainMessage,
unlockedKey: Unarmored,
encryptionKeyRing: KeyRing,
@ -290,7 +340,7 @@ class GOpenPGPCrypto : PGPCrypto {
}
}
private fun signEncrypted(
private fun signFileDetachedEncrypted(
source: File,
unlockedKey: Unarmored,
encryptionKeyRing: KeyRing,
@ -305,7 +355,11 @@ class GOpenPGPCrypto : PGPCrypto {
}
}
private fun verify(
// endregion
// region Private Verify
private fun verifyMessageDetached(
plainMessage: PlainMessage,
signature: Armored,
publicKey: Armored,
@ -316,21 +370,7 @@ class GOpenPGPCrypto : PGPCrypto {
publicKeyRing.verifyDetached(plainMessage, pgpSignature, validAtUtc)
}.isSuccess
private fun verify(
source: File,
signature: Armored,
publicKey: Armored,
validAtUtc: Long
): Boolean = runCatching {
source.inputStream().use { fileInputStream ->
val reader = Mobile2GoReader(fileInputStream.mobileReader())
val pgpSignature = PGPSignature(signature)
val publicKeyRing = newKeyRing(publicKey)
publicKeyRing.verifyDetachedStream(reader, pgpSignature, validAtUtc)
}
}.isSuccess
private fun verifyEncrypted(
private fun verifyMessageDetachedEncrypted(
plainMessage: PlainMessage,
encryptedSignature: EncryptedSignature,
decryptionKey: Unarmored,
@ -346,7 +386,21 @@ class GOpenPGPCrypto : PGPCrypto {
}
}.isSuccess
private fun verifyEncrypted(
private fun verifyFileDetached(
source: File,
signature: Armored,
publicKey: Armored,
validAtUtc: Long
): Boolean = runCatching {
source.inputStream().use { fileInputStream ->
val reader = Mobile2GoReader(fileInputStream.mobileReader())
val pgpSignature = PGPSignature(signature)
val publicKeyRing = newKeyRing(publicKey)
publicKeyRing.verifyDetachedStream(reader, pgpSignature, validAtUtc)
}
}.isSuccess
private fun verifyFileDetachedEncrypted(
source: File,
encryptedSignature: EncryptedSignature,
decryptionKey: Unarmored,
@ -367,7 +421,7 @@ class GOpenPGPCrypto : PGPCrypto {
// endregion
// region Lock/Unlock
// region Public Lock/Unlock
override fun lock(
unlockedKey: Unarmored,
@ -389,27 +443,27 @@ class GOpenPGPCrypto : PGPCrypto {
// endregion
// region Encrypt
// region Public Encrypt
override fun encryptText(
plainText: String,
publicKey: Armored
): EncryptedMessage = runCatching {
encrypt(PlainMessage(plainText), publicKey)
encryptMessage(PlainMessage(plainText), publicKey)
}.getOrElse { throw CryptoException("PlainText cannot be encrypted.", it) }
override fun encryptData(
data: ByteArray,
publicKey: Armored
): EncryptedMessage = runCatching {
encrypt(PlainMessage(data), publicKey)
encryptMessage(PlainMessage(data), publicKey)
}.getOrElse { throw CryptoException("Data cannot be encrypted.", it) }
override fun encryptData(
data: ByteArray,
sessionKey: SessionKey,
): DataPacket = runCatching {
encrypt(PlainMessage(data), sessionKey)
encryptMessageSessionKey(PlainMessage(data), sessionKey)
}.getOrElse { throw CryptoException("Data cannot be encrypted.", it) }
override fun encryptFile(
@ -417,7 +471,7 @@ class GOpenPGPCrypto : PGPCrypto {
destination: File,
sessionKey: SessionKey,
): EncryptedFile = runCatching {
encrypt(source, destination, sessionKey)
encryptFileSessionKey(source, destination, sessionKey)
}.getOrElse { throw CryptoException("File cannot be encrypted.", it) }
override fun encryptAndSignText(
@ -425,7 +479,7 @@ class GOpenPGPCrypto : PGPCrypto {
publicKey: Armored,
unlockedKey: Unarmored
): EncryptedMessage = runCatching {
encryptAndSign(PlainMessage(plainText), publicKey, unlockedKey)
encryptAndSignMessage(PlainMessage(plainText), publicKey, unlockedKey)
}.getOrElse { throw CryptoException("PlainText cannot be encrypted or signed.", it) }
override fun encryptAndSignData(
@ -433,7 +487,15 @@ class GOpenPGPCrypto : PGPCrypto {
publicKey: Armored,
unlockedKey: Unarmored
): EncryptedMessage = runCatching {
encryptAndSign(PlainMessage(data), publicKey, unlockedKey)
encryptAndSignMessage(PlainMessage(data), publicKey, unlockedKey)
}.getOrElse { throw CryptoException("Data cannot be encrypted or signed.", it) }
override fun encryptAndSignData(
data: ByteArray,
sessionKey: SessionKey,
unlockedKey: Unarmored
): DataPacket = runCatching {
encryptAndSignMessageSessionKey(PlainMessage(data), sessionKey, unlockedKey)
}.getOrElse { throw CryptoException("Data cannot be encrypted or signed.", it) }
override fun encryptAndSignFile(
@ -442,7 +504,7 @@ class GOpenPGPCrypto : PGPCrypto {
sessionKey: SessionKey,
unlockedKey: Unarmored
): EncryptedFile = runCatching {
encryptAndSign(source, destination, sessionKey, unlockedKey)
encryptAndSignFileSessionKey(source, destination, sessionKey, unlockedKey)
}.getOrElse { throw CryptoException("File cannot be encrypted or signed.", it) }
override fun encryptSessionKey(
@ -464,27 +526,27 @@ class GOpenPGPCrypto : PGPCrypto {
// endregion
// region Decrypt
// region Public Decrypt
override fun decryptText(
message: EncryptedMessage,
unlockedKey: Unarmored
): String = runCatching {
decrypt(message, unlockedKey) { it.string }
decryptMessage(message, unlockedKey) { it.string }
}.getOrElse { throw CryptoException("Message cannot be decrypted.", it) }
override fun decryptData(
message: EncryptedMessage,
unlockedKey: Unarmored
): ByteArray = runCatching {
decrypt(message, unlockedKey) { it.binary }
decryptMessage(message, unlockedKey) { it.binary }
}.getOrElse { throw CryptoException("Message cannot be decrypted.", it) }
override fun decryptData(
data: DataPacket,
sessionKey: SessionKey
): ByteArray = runCatching {
decrypt(data, sessionKey).binary
decryptDataSessionKey(data, sessionKey).binary
}.getOrElse { throw CryptoException("Data cannot be decrypted.", it) }
override fun decryptFile(
@ -492,7 +554,7 @@ class GOpenPGPCrypto : PGPCrypto {
destination: File,
sessionKey: SessionKey
): DecryptedFile = runCatching {
decrypt(source, destination, sessionKey)
decryptFileSessionKey(source, destination, sessionKey)
}.getOrElse { throw CryptoException("File cannot be decrypted.", it) }
override fun decryptAndVerifyText(
@ -501,7 +563,7 @@ class GOpenPGPCrypto : PGPCrypto {
unlockedKeys: List<Unarmored>,
time: VerificationTime,
): DecryptedText = runCatching {
decryptAndVerify(message, publicKeys, unlockedKeys, time.toUtcSeconds()) {
decryptAndVerifyMessage(message, publicKeys, unlockedKeys, time.toUtcSeconds()) {
DecryptedText(
it.message.string,
it.signatureVerificationError.toVerificationStatus()
@ -515,7 +577,21 @@ class GOpenPGPCrypto : PGPCrypto {
unlockedKeys: List<Unarmored>,
time: VerificationTime,
): DecryptedData = runCatching {
decryptAndVerify(message, publicKeys, unlockedKeys, time.toUtcSeconds()) {
decryptAndVerifyMessage(message, publicKeys, unlockedKeys, time.toUtcSeconds()) {
DecryptedData(
it.message.binary,
it.signatureVerificationError.toVerificationStatus()
)
}
}.getOrElse { throw CryptoException("Message cannot be decrypted.", it) }
override fun decryptAndVerifyData(
data: DataPacket,
sessionKey: SessionKey,
publicKeys: List<Armored>,
time: VerificationTime,
): DecryptedData = runCatching {
decryptAndVerifyDataSessionKey(data, sessionKey, publicKeys, time.toUtcSeconds()).let {
DecryptedData(
it.message.binary,
it.signatureVerificationError.toVerificationStatus()
@ -530,7 +606,7 @@ class GOpenPGPCrypto : PGPCrypto {
publicKeys: List<Armored>,
time: VerificationTime,
): DecryptedFile = runCatching {
decryptAndVerify(source, destination, sessionKey, publicKeys, time.toUtcSeconds())
decryptAndVerifyFileSessionKey(source, destination, sessionKey, publicKeys, time.toUtcSeconds())
}.getOrElse { throw CryptoException("File cannot be decrypted.", it) }
override fun decryptSessionKey(
@ -553,27 +629,27 @@ class GOpenPGPCrypto : PGPCrypto {
// endregion
// region Sign
// region Public Sign
override fun signText(
plainText: String,
unlockedKey: Unarmored
): Signature = runCatching {
sign(PlainMessage(plainText), unlockedKey)
signMessageDetached(PlainMessage(plainText), unlockedKey)
}.getOrElse { throw CryptoException("PlainText cannot be signed.", it) }
override fun signData(
data: ByteArray,
unlockedKey: Unarmored
): Signature = runCatching {
sign(PlainMessage(data), unlockedKey)
signMessageDetached(PlainMessage(data), unlockedKey)
}.getOrElse { throw CryptoException("Data cannot be signed.", it) }
override fun signFile(
file: File,
unlockedKey: Unarmored
): Signature = runCatching {
sign(file, unlockedKey)
signFileDetached(file, unlockedKey)
}.getOrElse { throw CryptoException("InputStream cannot be signed.", it) }
override fun signTextEncrypted(
@ -581,7 +657,7 @@ class GOpenPGPCrypto : PGPCrypto {
unlockedKey: Unarmored,
encryptionKeys: List<Armored>,
): EncryptedSignature = runCatching {
signEncrypted(PlainMessage(plainText), unlockedKey, newKeyRing(encryptionKeys))
signMessageDetachedEncrypted(PlainMessage(plainText), unlockedKey, newKeyRing(encryptionKeys))
}.getOrElse { throw CryptoException("PlainText cannot be signed.", it) }
override fun signDataEncrypted(
@ -589,7 +665,7 @@ class GOpenPGPCrypto : PGPCrypto {
unlockedKey: Unarmored,
encryptionKeys: List<Armored>,
): EncryptedSignature = runCatching {
signEncrypted(PlainMessage(data), unlockedKey, newKeyRing(encryptionKeys))
signMessageDetachedEncrypted(PlainMessage(data), unlockedKey, newKeyRing(encryptionKeys))
}.getOrElse { throw CryptoException("Data cannot be signed.", it) }
override fun signFileEncrypted(
@ -597,33 +673,33 @@ class GOpenPGPCrypto : PGPCrypto {
unlockedKey: Unarmored,
encryptionKeys: List<Armored>,
): EncryptedSignature = runCatching {
signEncrypted(file, unlockedKey, newKeyRing(encryptionKeys))
signFileDetachedEncrypted(file, unlockedKey, newKeyRing(encryptionKeys))
}.getOrElse { throw CryptoException("InputStream cannot be signed.", it) }
// endregion
// region Verify
// region Public Verify
override fun verifyText(
plainText: String,
signature: Armored,
publicKey: Armored,
time: VerificationTime,
): Boolean = verify(PlainMessage(plainText), signature, publicKey, time.toUtcSeconds())
): Boolean = verifyMessageDetached(PlainMessage(plainText), signature, publicKey, time.toUtcSeconds())
override fun verifyData(
data: ByteArray,
signature: Armored,
publicKey: Armored,
time: VerificationTime,
): Boolean = verify(PlainMessage(data), signature, publicKey, time.toUtcSeconds())
): Boolean = verifyMessageDetached(PlainMessage(data), signature, publicKey, time.toUtcSeconds())
override fun verifyFile(
file: DecryptedFile,
signature: Armored,
publicKey: Armored,
time: VerificationTime,
): Boolean = verify(file.file, signature, publicKey, time.toUtcSeconds())
): Boolean = verifyFileDetached(file.file, signature, publicKey, time.toUtcSeconds())
override fun verifyTextEncrypted(
plainText: String,
@ -632,7 +708,13 @@ class GOpenPGPCrypto : PGPCrypto {
publicKeys: List<Armored>,
time: VerificationTime,
): Boolean =
verifyEncrypted(PlainMessage(plainText), encryptedSignature, privateKey, publicKeys, time.toUtcSeconds())
verifyMessageDetachedEncrypted(
PlainMessage(plainText),
encryptedSignature,
privateKey,
publicKeys,
time.toUtcSeconds()
)
override fun verifyDataEncrypted(
data: ByteArray,
@ -641,7 +723,13 @@ class GOpenPGPCrypto : PGPCrypto {
publicKeys: List<Armored>,
time: VerificationTime,
): Boolean =
verifyEncrypted(PlainMessage(data), encryptedSignature, privateKey, publicKeys, time.toUtcSeconds())
verifyMessageDetachedEncrypted(
PlainMessage(data),
encryptedSignature,
privateKey,
publicKeys,
time.toUtcSeconds()
)
override fun verifyFileEncrypted(
file: File,
@ -650,11 +738,11 @@ class GOpenPGPCrypto : PGPCrypto {
publicKeys: List<Armored>,
time: VerificationTime,
): Boolean =
verifyEncrypted(file, encryptedSignature, privateKey, publicKeys, time.toUtcSeconds())
verifyFileDetachedEncrypted(file, encryptedSignature, privateKey, publicKeys, time.toUtcSeconds())
// endregion
// region Get
// region Public Get
override fun getArmored(
data: Unarmored
@ -717,7 +805,7 @@ class GOpenPGPCrypto : PGPCrypto {
// endregion
// region SessionKey/HashKey/PrivateKey/Token generation
// region Public SessionKey/HashKey/PrivateKey/Token generation
override fun generateNewSessionKey(): SessionKey {
return SessionKey(Crypto.generateSessionKey().key)
@ -771,7 +859,7 @@ class GOpenPGPCrypto : PGPCrypto {
// endregion
// region Time
// region Public Time
override fun updateTime(epochSeconds: Long) {
Crypto.updateTime(epochSeconds)

View File

@ -85,7 +85,9 @@ interface PGPCrypto {
fun encryptFile(source: File, destination: File, sessionKey: SessionKey): EncryptedFile
/**
* Encrypt [plainText] using [publicKey] and sign using [unlockedKey] in an embedded [EncryptedMessage].
* Encrypt [plainText] using [publicKey] and sign using [unlockedKey].
*
* @return [EncryptedMessage] with embedded signature.
*
* @throws [CryptoException] if [plainText] cannot be encrypted or signed.
*
@ -94,7 +96,9 @@ interface PGPCrypto {
fun encryptAndSignText(plainText: String, publicKey: Armored, unlockedKey: Unarmored): EncryptedMessage
/**
* Encrypt [data] using [publicKey] and sign using [unlockedKey] in an embedded [EncryptedMessage].
* Encrypt [data] using [publicKey] and sign using [unlockedKey].
*
* @return [EncryptedMessage] with embedded signature.
*
* @throws [CryptoException] if [data] cannot be encrypted or signed.
*
@ -102,6 +106,17 @@ interface PGPCrypto {
*/
fun encryptAndSignData(data: ByteArray, publicKey: Armored, unlockedKey: Unarmored): EncryptedMessage
/**
* Encrypt [data] using [sessionKey] and sign using [unlockedKey].
*
* @return [DataPacket] with embedded signature.
*
* @throws [CryptoException] if [data] cannot be encrypted or signed.
*
* @see [decryptAndVerifyData].
*/
fun encryptAndSignData(data: ByteArray, sessionKey: SessionKey, unlockedKey: Unarmored): DataPacket
/**
* Encrypt [source] into [destination] using [sessionKey] and sign using [unlockedKey].
*
@ -210,6 +225,24 @@ interface PGPCrypto {
time: VerificationTime = VerificationTime.Now
): DecryptedData
/**
* Decrypt [data] as [ByteArray] using [sessionKey] and verify using [publicKeys].
*
* @param time time for embedded signature validation, default to [VerificationTime.Now].
*
* @throws [CryptoException] if [data] cannot be decrypted.
*
* @see [DecryptedData]
* @see [VerificationStatus]
* @see [encryptAndSignData]
*/
fun decryptAndVerifyData(
data: DataPacket,
sessionKey: SessionKey,
publicKeys: List<Armored>,
time: VerificationTime = VerificationTime.Now
): DecryptedData
/**
* Decrypt [source] into [destination] using [sessionKey] and verify using [publicKeys].
*

View File

@ -174,6 +174,17 @@ fun PGPCrypto.encryptAndSignDataOrNull(
unlockedKey: Unarmored
): EncryptedMessage? = runCatching { encryptAndSignData(data, publicKey, unlockedKey) }.getOrNull()
/**
* @return [DataPacket], or `null` if [data] cannot be encrypted and signed.
*
* @see [PGPCrypto.encryptAndSignData]
*/
fun PGPCrypto.encryptAndSignDataOrNull(
data: ByteArray,
sessionKey: SessionKey,
publicKey: Unarmored,
): DataPacket? = runCatching { encryptAndSignData(data, sessionKey, publicKey) }.getOrNull()
/**
* @param time time for embedded signature validation, default to [VerificationTime.Now].
*
@ -202,6 +213,20 @@ fun PGPCrypto.decryptAndVerifyDataOrNull(
time: VerificationTime = VerificationTime.Now
): DecryptedData? = runCatching { decryptAndVerifyData(message, publicKeys, unlockedKeys, time) }.getOrNull()
/**
* @param time time for embedded signature validation, default to [VerificationTime.Now].
*
* @return [DecryptedData], or `null` if [data] cannot be decrypted.
*
* @see [PGPCrypto.decryptData]
*/
fun PGPCrypto.decryptAndVerifyDataOrNull(
data: DataPacket,
sessionKey: SessionKey,
publicKeys: List<Armored>,
time: VerificationTime = VerificationTime.Now,
): DecryptedData? = runCatching { decryptAndVerifyData(data, sessionKey, publicKeys, time) }.getOrNull()
/**
* @param time time for embedded signature validation, default to [VerificationTime.Now].
*

View File

@ -23,7 +23,7 @@ plugins {
kotlin("android")
}
libVersion = Version(1, 15, 7)
libVersion = Version(1, 15, 8)
android()

View File

@ -120,11 +120,10 @@ class KeyHolderTest {
keyHolder1.useKeys(context) {
val keyPacket = generateNewKeyPacket()
val encryptedText = encryptData(data, keyPacket)
val encryptedData = encryptData(data, keyPacket)
val decryptedData = decryptData(encryptedData, keyPacket)
val decryptedText = decryptData(encryptedText, keyPacket)
assertEquals(data, decryptedText)
assertTrue(data.contentEquals(decryptedData))
}
}
@ -135,11 +134,10 @@ class KeyHolderTest {
keyHolder1.useKeys(context) {
val sessionKey = generateNewSessionKey()
val encryptedText = encryptData(data, sessionKey)
val encryptedData = encryptData(data, sessionKey)
val decryptedData = decryptData(encryptedData, sessionKey)
val decryptedText = decryptData(encryptedText, sessionKey)
assertEquals(data, decryptedText)
assertTrue(data.contentEquals(decryptedData))
}
}

View File

@ -73,8 +73,9 @@ import java.io.InputStream
* @param context [CryptoContext] providing any needed dependencies for Crypto functions.
* @param block a function allowing usage of [KeyHolderContext] extension functions.
* @return the result of [block] function invoked on this [KeyHolder].
* @see [useKeysAs]
*/
fun <R> KeyHolder.useKeys(context: CryptoContext, block: KeyHolderContext.() -> R): R {
inline fun <R> KeyHolder.useKeys(context: CryptoContext, block: KeyHolderContext.() -> R): R {
val privateKeys = keys.map { key -> key.privateKey }
val publicKeys = privateKeys.map { key -> key.publicKey(context) }
val keyHolderContext = KeyHolderContext(
@ -85,6 +86,30 @@ fun <R> KeyHolder.useKeys(context: CryptoContext, block: KeyHolderContext.() ->
return keyHolderContext.use { block(it) }
}
/**
* Same as [useKeys] but without receiver, useful for nested keys usage.
*
* Executes the given [block] function for a [KeyHolder] on a [KeyHolderContext] and then close any associated
* key resources whether an exception is thrown or not.
*
* Example:
* ```
* main.useKeysAs(context) { mainContext ->
* nested.useKeysAs(context) { nestedContext ->
* val text = "text"
*
* val encryptedText = nestedContext.encryptText(text)
* val signedText = mainContext.signText(text)
* }
* }
* ```
* @param context [CryptoContext] providing any needed dependencies for Crypto functions.
* @param block a function allowing usage of [KeyHolderContext] extension functions.
* @return the result of [block] function invoked on this [KeyHolder].
* @see [useKeys]
*/
inline fun <R> KeyHolder.useKeysAs(context: CryptoContext, block: (KeyHolderContext) -> R): R = useKeys(context, block)
/**
* Decrypt [message] as [String] using [PrivateKeyRing].
*
@ -215,7 +240,7 @@ fun KeyHolderContext.decryptDataOrNull(message: EncryptedMessage): ByteArray? =
* @see [KeyHolderContext.decryptData]
*/
fun KeyHolderContext.decryptDataOrNull(data: DataPacket, sessionKey: SessionKey): ByteArray? =
context.pgpCrypto.decryptDataOrNull(data, sessionKey)
context.pgpCrypto.decryptDataOrNull(data, sessionKey)
/**
* Decrypt [source] into [destination] as [DecryptedFile] using [keyPacket].
@ -492,7 +517,7 @@ fun KeyHolderContext.encryptData(data: ByteArray): EncryptedMessage =
* @see [KeyHolderContext.decryptData]
*/
fun KeyHolderContext.encryptData(data: ByteArray, keyPacket: KeyPacket): DataPacket =
decryptSessionKey(keyPacket).use { key -> key.encryptData(context, data) }
decryptSessionKey(keyPacket).use { sessionKey -> sessionKey.encryptData(context, data) }
/**
* Encrypt [data] using [sessionKey].
@ -514,7 +539,7 @@ fun KeyHolderContext.encryptData(data: ByteArray, sessionKey: SessionKey): DataP
* @see [KeyHolderContext.decryptFile]
*/
fun KeyHolderContext.encryptFile(source: File, destination: File, keyPacket: KeyPacket): EncryptedFile =
decryptSessionKey(keyPacket).use { key -> encryptFile(source, destination, key) }
decryptSessionKey(keyPacket).use { sessionKey -> sessionKey.encryptFile(context, source, destination) }
/**
* Encrypt [source] into [destination] using [sessionKey].
@ -562,15 +587,20 @@ fun KeyHolderContext.encryptFile(fileName: String, data: ByteArray, keyPacket: K
encryptFile(fileName, ByteArrayInputStream(data), keyPacket)
/**
* Encrypt [text] using [encryptKeyRing] and sign using [KeyHolderContext.privateKeyRing] in an embedded [EncryptedMessage].
* Encrypt [text] using [encryptKeyRing] and sign using [KeyHolderContext.privateKeyRing].
*
* @param encryptKeyRing [PublicKeyRing] used to encrypt. Default: [KeyHolderContext.publicKeyRing].
*
* @return [EncryptedMessage] with embedded signature.
*
* @throws [CryptoException] if [text] cannot be encrypted or signed.
*
* @see [KeyHolderContext.decryptAndVerifyText].
*/
fun KeyHolderContext.encryptAndSignText(text: String, encryptKeyRing: PublicKeyRing = publicKeyRing): EncryptedMessage =
fun KeyHolderContext.encryptAndSignText(
text: String,
encryptKeyRing: PublicKeyRing = publicKeyRing
): EncryptedMessage =
context.pgpCrypto.encryptAndSignText(
text,
encryptKeyRing.primaryKey.key,
@ -578,10 +608,12 @@ fun KeyHolderContext.encryptAndSignText(text: String, encryptKeyRing: PublicKeyR
)
/**
* Encrypt [data] using [encryptKeyRing] and sign using [KeyHolderContext.privateKeyRing] in an embedded [EncryptedMessage].
* Encrypt [data] using [encryptKeyRing] and sign using [KeyHolderContext.privateKeyRing].
*
* @param encryptKeyRing [PublicKeyRing] used to encrypt. Default: [KeyHolderContext.publicKeyRing].
*
* @return [EncryptedMessage] with embedded signature.
*
* @throws [CryptoException] if [data] cannot be encrypted or signed.
*
* @see [KeyHolderContext.decryptAndVerifyData].
@ -597,31 +629,65 @@ fun KeyHolderContext.encryptAndSignData(
)
/**
* Encrypt [source] into [destination] using [keyPacket] and sign using [PrivateKeyRing].
* Encrypt [data] using [keyPacket] and sign using [KeyHolderContext.privateKeyRing].
*
* Note: If several operations use the same [keyPacket], prefer using [decryptSessionKey].
*
* @return [DataPacket] with embedded signature.
*
* @throws [CryptoException] if [data] cannot be encrypted.
*
* @see [KeyHolderContext.decryptAndVerifyData]
*/
fun KeyHolderContext.encryptAndSignData(
data: ByteArray,
keyPacket: KeyPacket
): DataPacket = decryptSessionKey(keyPacket).use { sessionKey ->
sessionKey.encryptAndSignData(context, data, privateKeyRing.unlockedPrimaryKey.unlockedKey.value)
}
/**
* Encrypt [data] using [sessionKey] and sign using [KeyHolderContext.privateKeyRing].
*
* @return [DataPacket] with embedded signature.
*
* @throws [CryptoException] if [data] cannot be encrypted.
*
* @see [KeyHolderContext.decryptAndVerifyData]
*/
fun KeyHolderContext.encryptAndSignData(
data: ByteArray,
sessionKey: SessionKey
): DataPacket =
sessionKey.encryptAndSignData(context, data, privateKeyRing.unlockedPrimaryKey.unlockedKey.value)
/**
* Encrypt [source] into [destination] using [keyPacket] and sign using [KeyHolderContext.privateKeyRing].
*
* @throws [CryptoException] if [source] cannot be encrypted or signed.
*
* @return [EncryptedFile] with embedded signature.
*
* If several operations use the same keyPacket, prefer `decryptSessionKey(keyPacket).use {...}`.
*
* @see [KeyHolderContext.decryptAndVerifyFile].
*/
fun KeyHolderContext.encryptAndSignFile(source: File, destination: File, keyPacket: KeyPacket): EncryptedFile =
decryptSessionKey(keyPacket).use { key -> encryptAndSignFile(source, destination, key) }
decryptSessionKey(keyPacket).use { sessionKey ->
sessionKey.encryptAndSignFile(context, source, destination, privateKeyRing.unlockedPrimaryKey.unlockedKey.value)
}
/**
* Encrypt [source] into [destination] using [sessionKey] and sign using [PrivateKeyRing].
* Encrypt [source] into [destination] using [sessionKey] and sign using [KeyHolderContext.privateKeyRing].
*
* @throws [CryptoException] if [source] cannot be encrypted or signed.
*
* @throws [CryptoException] if [source] cannot be encrypted or signed.
*
* @see [KeyHolderContext.decryptAndVerifyFile].
*/
fun KeyHolderContext.encryptAndSignFile(source: File, destination: File, sessionKey: SessionKey): EncryptedFile =
context.pgpCrypto.encryptAndSignFile(
source,
destination,
sessionKey,
privateKeyRing.unlockedPrimaryKey.unlockedKey.value
)
sessionKey.encryptAndSignFile(context, source, destination, privateKeyRing.unlockedPrimaryKey.unlockedKey.value)
/**
* Encrypt [sessionKey] using [PublicKeyRing].
@ -634,7 +700,7 @@ fun KeyHolderContext.encryptSessionKey(sessionKey: SessionKey): KeyPacket =
publicKeyRing.encryptSessionKey(context, sessionKey)
/**
* Encrypt [hashKey] using [encryptKeyRing] and sign using [KeyHolderContext.privateKeyRing] in an embedded [EncryptedMessage].
* Encrypt [hashKey] using [encryptKeyRing] and sign using [KeyHolderContext.privateKeyRing].
*
* @param encryptKeyRing [PublicKeyRing] used to encrypt. Default: [KeyHolderContext.publicKeyRing].
*
@ -694,27 +760,72 @@ fun KeyHolderContext.decryptAndVerifyData(
)
/**
* Decrypt [source] into [destination] using [keyPacket] and verify using [PublicKeyRing].
* Decrypt [data] as [ByteArray] using [keyPacket] and verify using [verifyKeyRing].
*
* Note: If several operations use the same [keyPacket], prefer using [decryptSessionKey].
*
* @param verifyKeyRing [PublicKeyRing] used to verify. Default: [KeyHolderContext.publicKeyRing].
* @param time time for embedded signature validation, default to [VerificationTime.Now].
*
* @throws [CryptoException] if [data] cannot be decrypted.
*
* @see [KeyHolderContext.decryptDataOrNull]
* @see [KeyHolderContext.encryptData]
*/
fun KeyHolderContext.decryptAndVerifyData(
data: DataPacket,
keyPacket: KeyPacket,
verifyKeyRing: PublicKeyRing = publicKeyRing,
time: VerificationTime = VerificationTime.Now,
): DecryptedData = decryptSessionKey(keyPacket).use { sessionKey ->
sessionKey.decryptAndVerifyData(context, data, verifyKeyRing.keys.map { it.key }, time)
}
/**
* Decrypt [data] as [ByteArray] using [sessionKey] and verify using [verifyKeyRing].
*
* @param verifyKeyRing [PublicKeyRing] used to verify, default to [KeyHolderContext.publicKeyRing].
* @param time time for embedded signature validation, default to [VerificationTime.Now].
*
* @throws [CryptoException] if [data] cannot be decrypted.
*
* @see [KeyHolderContext.decryptDataOrNull]
* @see [KeyHolderContext.encryptData]
*/
fun KeyHolderContext.decryptAndVerifyData(
data: DataPacket,
sessionKey: SessionKey,
verifyKeyRing: PublicKeyRing = publicKeyRing,
time: VerificationTime = VerificationTime.Now,
): DecryptedData =
sessionKey.decryptAndVerifyData(context, data, verifyKeyRing.keys.map { it.key }, time)
/**
* Decrypt [source] into [destination] using [keyPacket] and verify using [verifyKeyRing].
*
* Note: If several operations use the same [keyPacket], prefer using [decryptSessionKey].
*
* @param verifyKeyRing [PublicKeyRing] used to verify, default to [KeyHolderContext.publicKeyRing].
* @param time time for embedded signature validation, default to [VerificationTime.Now].
*
* @throws [CryptoException] if [source] cannot be decrypted.
*
* If several operations use the same keyPacket, prefer `decryptSessionKey(keyPacket).use {...}`.
*
* @see [KeyHolderContext.encryptAndSignFile]
*/
fun KeyHolderContext.decryptAndVerifyFile(
source: EncryptedFile,
destination: File,
keyPacket: KeyPacket,
verifyKeyRing: PublicKeyRing = publicKeyRing,
time: VerificationTime = VerificationTime.Now
): DecryptedFile =
decryptSessionKey(keyPacket).use { key -> decryptAndVerifyFile(source, destination, key, time) }
): DecryptedFile = decryptSessionKey(keyPacket).use { sessionKey ->
sessionKey.decryptAndVerifyFile(context, source, destination, verifyKeyRing.keys.map { it.key }, time)
}
/**
* Decrypt [source] into [destination] using [sessionKey] and verify using [PublicKeyRing].
* Decrypt [source] into [destination] using [sessionKey] and verify using [verifyKeyRing].
*
* @param verifyKeyRing [PublicKeyRing] used to verify, default to [KeyHolderContext.publicKeyRing].
* @param time time for embedded signature validation, default to [VerificationTime.Now].
*
* @throws [CryptoException] if [source] cannot be decrypted.
@ -725,22 +836,17 @@ fun KeyHolderContext.decryptAndVerifyFile(
source: EncryptedFile,
destination: File,
sessionKey: SessionKey,
verifyKeyRing: PublicKeyRing = publicKeyRing,
time: VerificationTime = VerificationTime.Now
): DecryptedFile =
context.pgpCrypto.decryptAndVerifyFile(
source,
destination,
sessionKey,
publicKeyRing.keys.map { it.key },
time
)
sessionKey.decryptAndVerifyFile(context, source, destination, verifyKeyRing.keys.map { it.key }, time)
/**
* Decrypt [message] as [String] using [KeyHolderContext.privateKeyRing] and verify using [verifyKeyRing].
*
* Note: String canonicalization/standardization is applied.
*
* @param verifyKeyRing [PublicKeyRing] used to verify. Default: [KeyHolderContext.publicKeyRing].
* @param verifyKeyRing [PublicKeyRing] used to verify, default to [KeyHolderContext.publicKeyRing].
* @param time time for embedded signature validation, default to [VerificationTime.Now].
*
* @return [DecryptedText], or `null` if [message] cannot be decrypted.
@ -762,7 +868,7 @@ fun KeyHolderContext.decryptAndVerifyTextOrNull(
/**
* Decrypt [message] as [ByteArray] using [KeyHolderContext.privateKeyRing] and verify using [verifyKeyRing].
*
* @param verifyKeyRing [PublicKeyRing] used to verify. Default: [KeyHolderContext.publicKeyRing].
* @param verifyKeyRing [PublicKeyRing] used to verify, default to [KeyHolderContext.publicKeyRing].
* @param time time for embedded signature validation, default to [VerificationTime.Now].
*
* @return [DecryptedData], or `null` if [message] cannot be decrypted.
@ -782,7 +888,27 @@ fun KeyHolderContext.decryptAndVerifyDataOrNull(
)
/**
* Decrypt [source] into [destination] using [keyPacket] and verify using [PublicKeyRing].
* Decrypt [data] using [sessionKey] and verify using [verifyKeyRing].
*
* @param verifyKeyRing [PublicKeyRing] used to verify, default to [KeyHolderContext.publicKeyRing].
* @param time time for embedded signature validation, default to [VerificationTime.Now].
*
* @return [DecryptedData], or `null` if [data] cannot be decrypted.
*
* @see [KeyHolderContext.decryptAndVerifyData]
*/
fun KeyHolderContext.decryptAndVerifyDataOrNull(
data: DataPacket,
sessionKey: SessionKey,
verifyKeyRing: PublicKeyRing = publicKeyRing,
time: VerificationTime = VerificationTime.Now
): DecryptedData? =
sessionKey.decryptAndVerifyDataOrNull(context, data, verifyKeyRing.keys.map { it.key }, time)
/**
* Decrypt [source] into [destination] using [keyPacket] and verify using [verifyKeyRing].
*
* Note: If several operations use the same [keyPacket], prefer using [decryptSessionKey].
*
* @param time time for embedded signature validation, default to [VerificationTime.Now].
*
@ -794,12 +920,14 @@ fun KeyHolderContext.decryptAndVerifyFileOrNull(
source: EncryptedFile,
destination: File,
keyPacket: KeyPacket,
verifyKeyRing: PublicKeyRing = publicKeyRing,
time: VerificationTime = VerificationTime.Now
): DecryptedFile? =
decryptSessionKeyOrNull(keyPacket)?.use { key -> decryptAndVerifyFileOrNull(source, destination, key, time) }
): DecryptedFile? = decryptSessionKeyOrNull(keyPacket)?.use { sessionKey ->
sessionKey.decryptAndVerifyFileOrNull(context, source, destination, verifyKeyRing.keys.map { it.key }, time)
}
/**
* Decrypt [source] into [destination] using [sessionKey] and verify using [PublicKeyRing].
* Decrypt [source] into [destination] using [sessionKey] and verify using [verifyKeyRing].
*
* @param time time for embedded signature validation, default to [VerificationTime.Now].
*
@ -811,15 +939,10 @@ fun KeyHolderContext.decryptAndVerifyFileOrNull(
source: EncryptedFile,
destination: File,
sessionKey: SessionKey,
verifyKeyRing: PublicKeyRing = publicKeyRing,
time: VerificationTime = VerificationTime.Now
): DecryptedFile? =
context.pgpCrypto.decryptAndVerifyFileOrNull(
source,
destination,
sessionKey,
publicKeyRing.keys.map { it.key },
time
)
sessionKey.decryptAndVerifyFileOrNull(context, source, destination, verifyKeyRing.keys.map { it.key }, time)
/**
* Get [Armored] from [Unarmored].

View File

@ -19,13 +19,20 @@
package me.proton.core.key.domain
import me.proton.core.crypto.common.context.CryptoContext
import me.proton.core.crypto.common.pgp.Armored
import me.proton.core.crypto.common.pgp.DataPacket
import me.proton.core.crypto.common.pgp.DecryptedData
import me.proton.core.crypto.common.pgp.DecryptedFile
import me.proton.core.crypto.common.pgp.EncryptedFile
import me.proton.core.crypto.common.pgp.SessionKey
import me.proton.core.crypto.common.pgp.Unarmored
import me.proton.core.crypto.common.pgp.VerificationTime
import me.proton.core.crypto.common.pgp.decryptAndVerifyDataOrNull
import me.proton.core.crypto.common.pgp.decryptAndVerifyFileOrNull
import me.proton.core.crypto.common.pgp.decryptDataOrNull
import me.proton.core.crypto.common.pgp.decryptFileOrNull
import me.proton.core.crypto.common.pgp.exception.CryptoException
import me.proton.core.key.domain.entity.keyholder.KeyHolderContext
import java.io.File
/**
@ -40,6 +47,19 @@ fun SessionKey.encryptData(
byteArray: ByteArray,
): DataPacket = context.pgpCrypto.encryptData(byteArray, this)
/**
* Encrypt [byteArray] using this [SessionKey] and sign using [unlockedKey].
*
* @throws [CryptoException] if [byteArray] cannot be encrypted.
*
* @see [KeyHolderContext.decryptData]
*/
fun SessionKey.encryptAndSignData(
context: CryptoContext,
byteArray: ByteArray,
unlockedKey: Unarmored,
): DataPacket = context.pgpCrypto.encryptAndSignData(byteArray, this, unlockedKey)
/**
* Encrypt [source] into [destination] using this [SessionKey].
*
@ -53,12 +73,27 @@ fun SessionKey.encryptFile(
destination: File,
): EncryptedFile = context.pgpCrypto.encryptFile(source, destination, this)
/**
* Encrypt [source] using this [SessionKey] and sign using [unlockedKey].
*
* @throws [CryptoException] if [source] cannot be encrypted.
*
* @see [KeyHolderContext.decryptAndVerifyFile]
*/
fun SessionKey.encryptAndSignFile(
context: CryptoContext,
source: File,
destination: File,
unlockedKey: Unarmored
): EncryptedFile = context.pgpCrypto.encryptAndSignFile(source, destination, this, unlockedKey)
/**
* Decrypt [data] using this [SessionKey].
*
* @throws [CryptoException] if [data] cannot be encrypted.
*
* @see [SessionKey.encryptData]
* @see [SessionKey.decryptDataOrNull]
*/
fun SessionKey.decryptData(
context: CryptoContext,
@ -66,16 +101,21 @@ fun SessionKey.decryptData(
): ByteArray = context.pgpCrypto.decryptData(data, this)
/**
* Decrypt [data] using this [SessionKey].
* Decrypt [data] using this [SessionKey] and verify using [publicKeys].
*
* @return [ByteArray], or `null` if [data] cannot be decrypted.
* @param time time for embedded signature validation, default to [VerificationTime.Now].
*
* @see [SessionKey.decryptData]
* @throws [CryptoException] if [data] cannot be encrypted.
*
* @see [SessionKey.encryptAndSignData]
* @see [SessionKey.decryptAndVerifyDataOrNull]
*/
fun SessionKey.decryptDataOrNull(
fun SessionKey.decryptAndVerifyData(
context: CryptoContext,
data: DataPacket,
): ByteArray? = context.pgpCrypto.decryptDataOrNull(data, this)
publicKeys: List<Armored>,
time: VerificationTime = VerificationTime.Now
): DecryptedData = context.pgpCrypto.decryptAndVerifyData(data, this, publicKeys, time)
/**
* Decrypt [source] into [destination] using this [SessionKey].
@ -83,6 +123,7 @@ fun SessionKey.decryptDataOrNull(
* @throws [CryptoException] if [source] cannot be encrypted.
*
* @see [SessionKey.encryptFile]
* @see [SessionKey.decryptFileOrNull]
*/
fun SessionKey.decryptFile(
context: CryptoContext,
@ -90,15 +131,78 @@ fun SessionKey.decryptFile(
destination: File
): DecryptedFile = context.pgpCrypto.decryptFile(source, destination, this)
/**
* Decrypt [source] using this [SessionKey] and verify using [publicKeys].
*
* @param time time for embedded signature validation, default to [VerificationTime.Now].
*
* @throws [CryptoException] if [source] cannot be encrypted.
*
* @see [SessionKey.encryptAndSignFile]
* @see [SessionKey.decryptAndVerifyFileOrNull]
*/
fun SessionKey.decryptAndVerifyFile(
context: CryptoContext,
source: EncryptedFile,
destination: File,
publicKeys: List<Armored>,
time: VerificationTime = VerificationTime.Now
): DecryptedFile = context.pgpCrypto.decryptAndVerifyFile(source, destination, this, publicKeys, time)
/**
* Decrypt [data] using this [SessionKey].
*
* @return [ByteArray], or `null` if [data] cannot be decrypted.
*
* @see [SessionKey.encryptData]
* @see [SessionKey.decryptData]
*/
fun SessionKey.decryptDataOrNull(
context: CryptoContext,
data: DataPacket,
): ByteArray? = context.pgpCrypto.decryptDataOrNull(data, this)
/**
* Decrypt [data] using this [SessionKey] and verify using [publicKeys].
*
* @return [DecryptedFile], or `null` if [data] cannot be decrypted.
*
* @see [SessionKey.encryptAndSignData]
* @see [SessionKey.decryptAndVerifyData]
*/
fun SessionKey.decryptAndVerifyDataOrNull(
context: CryptoContext,
data: DataPacket,
publicKeys: List<Armored>,
time: VerificationTime = VerificationTime.Now
): DecryptedData? = context.pgpCrypto.decryptAndVerifyDataOrNull(data, this, publicKeys, time)
/**
* Decrypt [source] into [destination] using this [SessionKey].
*
* @return [DecryptedFile], or `null` if [source] cannot be decrypted.
*
* @see [SessionKey.encryptFile]
* @see [SessionKey.decryptFile]
*/
fun SessionKey.decryptFileOrNull(
context: CryptoContext,
source: EncryptedFile,
destination: File
): DecryptedFile? = context.pgpCrypto.decryptFileOrNull(source, destination, this)
/**
* Decrypt [source] using this [SessionKey] and verify using [publicKeys].
*
* @return [DecryptedFile], or `null` if [source] cannot be decrypted.
*
* @see [SessionKey.encryptAndSignFile]
* @see [SessionKey.decryptAndVerifyFile]
*/
fun SessionKey.decryptAndVerifyFileOrNull(
context: CryptoContext,
source: EncryptedFile,
destination: File,
publicKeys: List<Armored>,
time: VerificationTime = VerificationTime.Now
): DecryptedFile? = context.pgpCrypto.decryptAndVerifyFileOrNull(source, destination, this, publicKeys, time)

View File

@ -267,6 +267,10 @@ class TestCryptoContext : CryptoContext {
"BINARY([${data.fromByteArray()}]+$publicKey+${unlockedKey.fromByteArray()})"
.encryptMessage(unlockedKey)
override fun encryptAndSignData(data: ByteArray, sessionKey: SessionKey, unlockedKey: Unarmored): DataPacket =
"BINARY([${data.fromByteArray()}]+${sessionKey.key}+${unlockedKey.fromByteArray()})"
.encryptMessage(unlockedKey).toByteArray()
override fun encryptAndSignFile(
source: File,
destination: File,
@ -306,6 +310,19 @@ class TestCryptoContext : CryptoContext {
VerificationStatus.Success
)
override fun decryptAndVerifyData(
data: DataPacket,
sessionKey: SessionKey,
publicKeys: List<Armored>,
time: VerificationTime,
): DecryptedData = DecryptedData(
data.decrypt(sessionKey.key).let { decrypted ->
check(String(decrypted).startsWith("BINARY"))
decrypted
},
VerificationStatus.Success
)
override fun decryptAndVerifyFile(
source: EncryptedFile,
destination: File,