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:
commit
946e1d2a3d
|
@ -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)
|
||||
|
|
|
@ -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].
|
||||
*
|
||||
|
|
|
@ -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].
|
||||
*
|
||||
|
|
|
@ -23,7 +23,7 @@ plugins {
|
|||
kotlin("android")
|
||||
}
|
||||
|
||||
libVersion = Version(1, 15, 7)
|
||||
libVersion = Version(1, 15, 8)
|
||||
|
||||
android()
|
||||
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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].
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue