forked from GitHub-Mirror/riotX-android
Decrypt Attachment - WIP
This commit is contained in:
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2016 OpenMarket Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.matrix.android.internal.crypto.attachments
|
||||
|
||||
import android.os.Parcelable
|
||||
import im.vector.matrix.android.internal.crypto.model.rest.EncryptedFileInfo
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
|
||||
|
||||
fun EncryptedFileInfo.toElementToDecrypt(): ElementToDecrypt? {
|
||||
// Check the validity of some fields
|
||||
if (isValid()) {
|
||||
return ElementToDecrypt(
|
||||
iv = this.iv!!,
|
||||
k = this.key!!.k!!,
|
||||
sha256 = this.hashes!!["sha256"] ?: error("")
|
||||
)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Represent data to decode an attachment
|
||||
*/
|
||||
@Parcelize
|
||||
data class ElementToDecrypt(
|
||||
val iv: String,
|
||||
val k: String,
|
||||
val sha256: String
|
||||
) : Parcelable
|
@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.matrix.android.internal.crypto
|
||||
package im.vector.matrix.android.internal.crypto.attachments
|
||||
|
||||
import android.text.TextUtils
|
||||
import android.util.Base64
|
||||
@ -142,24 +142,27 @@ object MXEncryptedAttachments {
|
||||
* @return the decrypted attachment stream
|
||||
*/
|
||||
fun decryptAttachment(attachmentStream: InputStream?, encryptedFileInfo: EncryptedFileInfo?): InputStream? {
|
||||
if (encryptedFileInfo?.isValid() != true) {
|
||||
Timber.e("## decryptAttachment() : some fields are not defined, or invalid key fields")
|
||||
return null
|
||||
}
|
||||
|
||||
val elementToDecrypt = encryptedFileInfo.toElementToDecrypt()
|
||||
|
||||
return decryptAttachment(attachmentStream, elementToDecrypt)
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypt an attachment
|
||||
*
|
||||
* @param attachmentStream the attachment stream
|
||||
* @param elementToDecrypt the elementToDecrypt info
|
||||
* @return the decrypted attachment stream
|
||||
*/
|
||||
fun decryptAttachment(attachmentStream: InputStream?, elementToDecrypt: ElementToDecrypt?): InputStream? {
|
||||
// sanity checks
|
||||
if (null == attachmentStream || null == encryptedFileInfo) {
|
||||
Timber.e("## decryptAttachment() : null parameters")
|
||||
return null
|
||||
}
|
||||
|
||||
if (TextUtils.isEmpty(encryptedFileInfo.iv)
|
||||
|| null == encryptedFileInfo.key
|
||||
|| null == encryptedFileInfo.hashes
|
||||
|| !encryptedFileInfo.hashes.containsKey("sha256")) {
|
||||
Timber.e("## decryptAttachment() : some fields are not defined")
|
||||
return null
|
||||
}
|
||||
|
||||
if (!TextUtils.equals(encryptedFileInfo.key!!.alg, "A256CTR")
|
||||
|| !TextUtils.equals(encryptedFileInfo.key!!.kty, "oct")
|
||||
|| TextUtils.isEmpty(encryptedFileInfo.key!!.k)) {
|
||||
Timber.e("## decryptAttachment() : invalid key fields")
|
||||
if (null == attachmentStream || elementToDecrypt == null) {
|
||||
Timber.e("## decryptAttachment() : null stream")
|
||||
return null
|
||||
}
|
||||
|
||||
@ -177,8 +180,8 @@ object MXEncryptedAttachments {
|
||||
val outStream = ByteArrayOutputStream()
|
||||
|
||||
try {
|
||||
val key = Base64.decode(base64UrlToBase64(encryptedFileInfo.key!!.k), Base64.DEFAULT)
|
||||
val initVectorBytes = Base64.decode(encryptedFileInfo.iv, Base64.DEFAULT)
|
||||
val key = Base64.decode(base64UrlToBase64(elementToDecrypt.k), Base64.DEFAULT)
|
||||
val initVectorBytes = Base64.decode(elementToDecrypt.iv, Base64.DEFAULT)
|
||||
|
||||
val decryptCipher = Cipher.getInstance(CIPHER_ALGORITHM)
|
||||
val secretKeySpec = SecretKeySpec(key, SECRET_KEY_SPEC_ALGORITHM)
|
||||
@ -205,7 +208,7 @@ object MXEncryptedAttachments {
|
||||
|
||||
val currentDigestValue = base64ToUnpaddedBase64(Base64.encodeToString(messageDigest.digest(), Base64.DEFAULT))
|
||||
|
||||
if (!TextUtils.equals(encryptedFileInfo.hashes["sha256"], currentDigestValue)) {
|
||||
if (!TextUtils.equals(elementToDecrypt.sha256, currentDigestValue)) {
|
||||
Timber.e("## decryptAttachment() : Digest value mismatch")
|
||||
outStream.close()
|
||||
return null
|
@ -33,7 +33,7 @@ data class EncryptedFileInfo(
|
||||
* Not documented
|
||||
*/
|
||||
@Json(name = "mimetype")
|
||||
var mimetype: String,
|
||||
var mimetype: String? = null,
|
||||
|
||||
/**
|
||||
* Required. A JSON Web Key object.
|
||||
@ -45,18 +45,45 @@ data class EncryptedFileInfo(
|
||||
* Required. The Initialisation Vector used by AES-CTR, encoded as unpadded base64.
|
||||
*/
|
||||
@Json(name = "iv")
|
||||
var iv: String,
|
||||
var iv: String? = null,
|
||||
|
||||
/**
|
||||
* Required. A map from an algorithm name to a hash of the ciphertext, encoded as unpadded base64.
|
||||
* Clients should support the SHA-256 hash, which uses the key "sha256".
|
||||
*/
|
||||
@Json(name = "hashes")
|
||||
var hashes: Map<String, String>,
|
||||
var hashes: Map<String, String>? = null,
|
||||
|
||||
/**
|
||||
* Required. Version of the encrypted attachments protocol. Must be "v2".
|
||||
*/
|
||||
@Json(name = "v")
|
||||
var v: String? = null
|
||||
)
|
||||
) {
|
||||
/**
|
||||
* Check what the spec tells us
|
||||
*/
|
||||
fun isValid(): Boolean {
|
||||
if (url.isNullOrBlank()) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (key?.isValid() != true) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (iv.isNullOrBlank()) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (hashes?.containsKey("sha256") != true) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (v != "v2") {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ data class EncryptedFileKey(
|
||||
* Required. Algorithm. Must be "A256CTR".
|
||||
*/
|
||||
@Json(name = "alg")
|
||||
var alg: String,
|
||||
var alg: String? = null,
|
||||
|
||||
/**
|
||||
* Required. Extractable. Must be true. This is a W3C extension.
|
||||
@ -36,18 +36,45 @@ data class EncryptedFileKey(
|
||||
* Required. Key operations. Must at least contain "encrypt" and "decrypt".
|
||||
*/
|
||||
@Json(name = "key_ops")
|
||||
var key_ops: List<String>,
|
||||
var key_ops: List<String>? = null,
|
||||
|
||||
/**
|
||||
* Required. Key type. Must be "oct".
|
||||
*/
|
||||
@Json(name = "kty")
|
||||
var kty: String,
|
||||
var kty: String? = null,
|
||||
|
||||
/**
|
||||
* Required. The key, encoded as urlsafe unpadded base64.
|
||||
*/
|
||||
@Json(name = "k")
|
||||
var k: String
|
||||
)
|
||||
var k: String? = null
|
||||
) {
|
||||
/**
|
||||
* Check what the spec tells us
|
||||
*/
|
||||
fun isValid(): Boolean {
|
||||
if (alg != "A256CTR") {
|
||||
return false
|
||||
}
|
||||
|
||||
if (ext != true) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (key_ops?.contains("encrypt") != true || key_ops?.contains("decrypt") != true) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (kty != "oct") {
|
||||
return false
|
||||
}
|
||||
|
||||
if (k.isNullOrBlank()) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user