EncryptionUtilsV2.kt 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098
  1. /*
  2. * Nextcloud - Android Client
  3. *
  4. * SPDX-FileCopyrightText: 2023 Tobias Kaminsky <tobias@kaminsky.me>
  5. * SPDX-FileCopyrightText: 2023 Nextcloud GmbH
  6. * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only
  7. */
  8. package com.owncloud.android.utils
  9. import android.accounts.AccountManager
  10. import android.content.Context
  11. import androidx.annotation.VisibleForTesting
  12. import com.google.gson.reflect.TypeToken
  13. import com.nextcloud.client.account.User
  14. import com.owncloud.android.MainApp
  15. import com.owncloud.android.R
  16. import com.owncloud.android.datamodel.ArbitraryDataProvider
  17. import com.owncloud.android.datamodel.ArbitraryDataProviderImpl
  18. import com.owncloud.android.datamodel.FileDataStorageManager
  19. import com.owncloud.android.datamodel.OCFile
  20. import com.owncloud.android.datamodel.e2e.v1.decrypted.DecryptedFolderMetadataFileV1
  21. import com.owncloud.android.datamodel.e2e.v1.encrypted.EncryptedFolderMetadataFileV1
  22. import com.owncloud.android.datamodel.e2e.v2.decrypted.DecryptedFile
  23. import com.owncloud.android.datamodel.e2e.v2.decrypted.DecryptedFolderMetadataFile
  24. import com.owncloud.android.datamodel.e2e.v2.decrypted.DecryptedMetadata
  25. import com.owncloud.android.datamodel.e2e.v2.decrypted.DecryptedUser
  26. import com.owncloud.android.datamodel.e2e.v2.encrypted.EncryptedFiledrop
  27. import com.owncloud.android.datamodel.e2e.v2.encrypted.EncryptedFolderMetadataFile
  28. import com.owncloud.android.datamodel.e2e.v2.encrypted.EncryptedMetadata
  29. import com.owncloud.android.datamodel.e2e.v2.encrypted.EncryptedUser
  30. import com.owncloud.android.lib.common.OwnCloudClient
  31. import com.owncloud.android.lib.common.accounts.AccountUtils
  32. import com.owncloud.android.lib.common.operations.RemoteOperationResult
  33. import com.owncloud.android.lib.common.utils.Log_OC
  34. import com.owncloud.android.lib.resources.e2ee.GetMetadataRemoteOperation
  35. import com.owncloud.android.lib.resources.e2ee.MetadataResponse
  36. import com.owncloud.android.lib.resources.e2ee.StoreMetadataV2RemoteOperation
  37. import com.owncloud.android.lib.resources.e2ee.UpdateMetadataV2RemoteOperation
  38. import com.owncloud.android.operations.UploadException
  39. import org.apache.commons.httpclient.HttpStatus
  40. import org.bouncycastle.asn1.ASN1Sequence
  41. import org.bouncycastle.asn1.cms.ContentInfo
  42. import org.bouncycastle.cert.jcajce.JcaCertStore
  43. import org.bouncycastle.cms.CMSProcessableByteArray
  44. import org.bouncycastle.cms.CMSSignedData
  45. import org.bouncycastle.cms.CMSSignedDataGenerator
  46. import org.bouncycastle.cms.SignerInformation
  47. import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder
  48. import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder
  49. import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder
  50. import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder
  51. import java.io.BufferedReader
  52. import java.io.ByteArrayOutputStream
  53. import java.io.InputStream
  54. import java.io.InputStreamReader
  55. import java.math.BigInteger
  56. import java.security.MessageDigest
  57. import java.security.PrivateKey
  58. import java.security.cert.X509Certificate
  59. import java.util.zip.GZIPInputStream
  60. import java.util.zip.GZIPOutputStream
  61. @Suppress("TooManyFunctions", "LargeClass")
  62. class EncryptionUtilsV2 {
  63. @VisibleForTesting
  64. fun encryptMetadata(metadata: DecryptedMetadata, metadataKey: ByteArray): EncryptedMetadata {
  65. val json = EncryptionUtils.serializeJSON(metadata, true)
  66. val gzip = gZipCompress(json)
  67. return EncryptionUtils.encryptStringSymmetric(
  68. gzip,
  69. metadataKey,
  70. EncryptionUtils.ivDelimiter
  71. )
  72. }
  73. @VisibleForTesting
  74. fun decryptMetadata(metadata: EncryptedMetadata, metadataKey: ByteArray): DecryptedMetadata {
  75. val decrypted = EncryptionUtils.decryptStringSymmetric(
  76. metadata.ciphertext,
  77. metadataKey,
  78. metadata.authenticationTag,
  79. metadata.nonce
  80. )
  81. val json = gZipDecompress(decrypted)
  82. val decryptedMetadata = EncryptionUtils.deserializeJSON(json, object : TypeToken<DecryptedMetadata>() {})
  83. decryptedMetadata.metadataKey = metadataKey
  84. return decryptedMetadata
  85. }
  86. @Suppress("LongParameterList")
  87. fun encryptFolderMetadataFile(
  88. metadataFile: DecryptedFolderMetadataFile,
  89. userId: String,
  90. folder: OCFile,
  91. storageManager: FileDataStorageManager,
  92. client: OwnCloudClient,
  93. privateKey: String,
  94. user: User,
  95. context: Context,
  96. arbitraryDataProvider: ArbitraryDataProvider
  97. ): EncryptedFolderMetadataFile {
  98. val encryptedUsers: List<EncryptedUser>
  99. val encryptedMetadata: EncryptedMetadata
  100. if (metadataFile.users.isEmpty()) {
  101. // we are in a subfolder, re-use users array
  102. val key = retrieveTopMostMetadataKey(
  103. folder,
  104. storageManager,
  105. client,
  106. userId,
  107. privateKey,
  108. user,
  109. context,
  110. arbitraryDataProvider
  111. )
  112. // do not store metadata key
  113. metadataFile.metadata.metadataKey = ByteArray(0)
  114. metadataFile.metadata.keyChecksums.clear()
  115. encryptedUsers = emptyList()
  116. encryptedMetadata = encryptMetadata(metadataFile.metadata, key)
  117. } else {
  118. encryptedUsers = metadataFile.users.map {
  119. encryptUser(
  120. it,
  121. metadataFile.metadata.metadataKey
  122. )
  123. }
  124. encryptedMetadata = encryptMetadata(metadataFile.metadata, metadataFile.metadata.metadataKey)
  125. }
  126. return EncryptedFolderMetadataFile(
  127. encryptedMetadata,
  128. encryptedUsers,
  129. mutableMapOf()
  130. )
  131. // if (metadataFile.users.isEmpty()) {
  132. // // we are in a subfolder, re-use users array
  133. // retrieveTopMostMetadata(
  134. // ocFile,
  135. // storageManager,
  136. // client
  137. // )
  138. // } else {
  139. // val encryptedUsers = metadataFile.users.map {
  140. // encryptUser(it, metadataFile.metadata.metadataKey)
  141. // }
  142. //
  143. // return EncryptedFolderMetadataFile(
  144. // encryptedMetadata,
  145. // encryptedUsers,
  146. // emptyMap()
  147. // )
  148. // }
  149. }
  150. @Throws(IllegalStateException::class, UploadException::class, Throwable::class)
  151. @Suppress("LongParameterList", "LongMethod", "ThrowsCount")
  152. fun decryptFolderMetadataFile(
  153. metadataFile: EncryptedFolderMetadataFile,
  154. userId: String,
  155. privateKey: String,
  156. ocFile: OCFile,
  157. storageManager: FileDataStorageManager,
  158. client: OwnCloudClient,
  159. oldCounter: Long,
  160. signature: String,
  161. user: User,
  162. context: Context,
  163. arbitraryDataProvider: ArbitraryDataProvider
  164. ): DecryptedFolderMetadataFile {
  165. val parent =
  166. storageManager.getFileById(ocFile.parentId) ?: throw IllegalStateException("Cannot retrieve metadata")
  167. var filesDropCountBefore = 0
  168. var filesBefore = 0
  169. val decryptedFolderMetadataFile = if (parent.isEncrypted) {
  170. // we are in a subfolder, decrypt information is in top most encrypted folder
  171. val topMostMetadata = retrieveTopMostMetadata(
  172. ocFile,
  173. storageManager,
  174. client,
  175. userId,
  176. privateKey,
  177. user,
  178. context,
  179. arbitraryDataProvider
  180. )
  181. val decryptedMetadata = decryptMetadata(metadataFile.metadata, topMostMetadata.metadata.metadataKey)
  182. decryptedMetadata.metadataKey = topMostMetadata.metadata.metadataKey
  183. decryptedMetadata.keyChecksums.addAll(topMostMetadata.metadata.keyChecksums)
  184. DecryptedFolderMetadataFile(
  185. decryptedMetadata,
  186. // subfolder do not store user array
  187. mutableListOf(),
  188. mutableMapOf()
  189. )
  190. } else {
  191. // Top folder
  192. val encryptedUser = metadataFile.users.find { it.userId == userId }
  193. ?: throw IllegalStateException("Cannot find current user in metadata")
  194. val decryptedMetadataKey = decryptMetadataKey(encryptedUser, privateKey)
  195. val users = metadataFile.users.map { transformUser(it) }.toMutableList()
  196. val decryptedMetadata = decryptMetadata(
  197. metadataFile.metadata,
  198. decryptedMetadataKey
  199. )
  200. // only top folder can have files drop
  201. filesBefore = decryptedMetadata.files.size
  202. if (metadataFile.filedrop != null) {
  203. filesDropCountBefore = metadataFile.filedrop.size
  204. }
  205. val fileDrop = metadataFile.filedrop
  206. if (fileDrop != null) {
  207. for (entry in fileDrop) {
  208. val key = entry.key
  209. val encryptedFile = entry.value
  210. val decryptedFile = decryptFiledrop(
  211. encryptedFile,
  212. privateKey,
  213. arbitraryDataProvider,
  214. user
  215. )
  216. decryptedMetadata.files[key] = decryptedFile
  217. }
  218. }
  219. DecryptedFolderMetadataFile(
  220. decryptedMetadata,
  221. users,
  222. mutableMapOf()
  223. )
  224. }
  225. verifyMetadata(metadataFile, decryptedFolderMetadataFile, oldCounter, signature)
  226. val transferredFiledrop = filesDropCountBefore > 0 &&
  227. decryptedFolderMetadataFile.metadata.files.size == filesBefore + filesDropCountBefore
  228. if (transferredFiledrop) {
  229. // lock folder
  230. val token = EncryptionUtils.lockFolder(ocFile, client)
  231. serializeAndUploadMetadata(
  232. ocFile,
  233. decryptedFolderMetadataFile,
  234. token,
  235. client,
  236. true,
  237. context,
  238. user,
  239. storageManager
  240. )
  241. // unlock folder
  242. val unlockFolderResult: RemoteOperationResult<*> = EncryptionUtils.unlockFolder(ocFile, client, token)
  243. if (!unlockFolderResult.isSuccess) {
  244. Log_OC.e(TAG, unlockFolderResult.message)
  245. throw IllegalStateException()
  246. }
  247. }
  248. return decryptedFolderMetadataFile
  249. }
  250. @Throws(Throwable::class)
  251. fun decryptFiledrop(
  252. filedrop: EncryptedFiledrop,
  253. privateKey: String,
  254. arbitraryDataProvider: ArbitraryDataProvider,
  255. user: User
  256. ): DecryptedFile {
  257. // decrypt key
  258. val encryptedKey = EncryptionUtils.decryptStringAsymmetricAsBytes(
  259. filedrop.users[0].encryptedFiledropKey,
  260. privateKey
  261. )
  262. // decrypt encrypted blob with key
  263. val decryptedData = EncryptionUtils.decryptStringSymmetricAsString(
  264. filedrop.ciphertext,
  265. encryptedKey,
  266. EncryptionUtils.decodeStringToBase64Bytes(filedrop.nonce),
  267. EncryptionUtils.decodeStringToBase64Bytes(filedrop.authenticationTag),
  268. true,
  269. arbitraryDataProvider,
  270. user
  271. )
  272. return EncryptionUtils.deserializeJSON(
  273. decryptedData,
  274. object : TypeToken<DecryptedFile>() {}
  275. )
  276. }
  277. @Throws(IllegalStateException::class)
  278. @Suppress("ThrowsCount", "LongParameterList")
  279. fun retrieveTopMostMetadata(
  280. folder: OCFile,
  281. storageManager: FileDataStorageManager,
  282. client: OwnCloudClient,
  283. userId: String,
  284. privateKey: String,
  285. user: User,
  286. context: Context,
  287. arbitraryDataProvider: ArbitraryDataProvider
  288. ): DecryptedFolderMetadataFile {
  289. var topMost = folder
  290. var parent =
  291. storageManager.getFileById(topMost.parentId) ?: throw IllegalStateException("Cannot retrieve metadata")
  292. while (parent.isEncrypted) {
  293. topMost = parent
  294. parent =
  295. storageManager.getFileById(topMost.parentId) ?: throw IllegalStateException("Cannot retrieve metadata")
  296. }
  297. // parent is now top most encrypted folder
  298. val result = GetMetadataRemoteOperation(topMost.localId).execute(client)
  299. if (result.isSuccess) {
  300. val v2 = EncryptionUtils.deserializeJSON(
  301. result.resultData.metadata,
  302. object : TypeToken<EncryptedFolderMetadataFile>() {}
  303. )
  304. return decryptFolderMetadataFile(
  305. v2,
  306. userId,
  307. privateKey,
  308. topMost,
  309. storageManager,
  310. client,
  311. topMost.e2eCounter,
  312. result.resultData.signature,
  313. user,
  314. context,
  315. arbitraryDataProvider
  316. )
  317. } else {
  318. throw IllegalStateException("Cannot retrieve metadata")
  319. }
  320. }
  321. @Throws(IllegalStateException::class)
  322. @Suppress("ThrowsCount", "LongParameterList")
  323. fun retrieveTopMostMetadataKey(
  324. folder: OCFile,
  325. storageManager: FileDataStorageManager,
  326. client: OwnCloudClient,
  327. userId: String,
  328. privateKey: String,
  329. user: User,
  330. context: Context,
  331. arbitraryDataProvider: ArbitraryDataProvider
  332. ): ByteArray {
  333. return retrieveTopMostMetadata(
  334. folder,
  335. storageManager,
  336. client,
  337. userId,
  338. privateKey,
  339. user,
  340. context,
  341. arbitraryDataProvider
  342. )
  343. .metadata.metadataKey
  344. }
  345. @VisibleForTesting
  346. fun encryptUser(user: DecryptedUser, metadataKey: ByteArray): EncryptedUser {
  347. val encryptedKey = EncryptionUtils.encryptStringAsymmetricV2(
  348. metadataKey,
  349. user.certificate
  350. )
  351. return EncryptedUser(
  352. user.userId,
  353. user.certificate,
  354. encryptedKey
  355. )
  356. }
  357. @VisibleForTesting
  358. fun transformUser(user: EncryptedUser): DecryptedUser {
  359. return DecryptedUser(
  360. user.userId,
  361. user.certificate,
  362. user.encryptedMetadataKey
  363. )
  364. }
  365. @VisibleForTesting
  366. fun decryptMetadataKey(user: EncryptedUser, privateKey: String): ByteArray {
  367. return EncryptionUtils.decryptStringAsymmetricV2(
  368. user.encryptedMetadataKey,
  369. privateKey
  370. )
  371. }
  372. fun gZipCompress(string: String): ByteArray {
  373. val outputStream = ByteArrayOutputStream()
  374. GZIPOutputStream(outputStream).apply {
  375. write(string.toByteArray())
  376. flush()
  377. close()
  378. }
  379. return outputStream.toByteArray()
  380. }
  381. fun gZipDecompress(compressed: String): String {
  382. return gZipDecompress(compressed.byteInputStream())
  383. }
  384. fun gZipDecompress(compressed: ByteArray): String {
  385. return gZipDecompress(compressed.inputStream())
  386. }
  387. @VisibleForTesting
  388. fun gZipDecompress(inputStream: InputStream): String {
  389. val stringBuilder = StringBuilder()
  390. val inputStream = GZIPInputStream(inputStream)
  391. // val inputStream = compressed.inputStream()
  392. val bufferedReader = BufferedReader(InputStreamReader(inputStream))
  393. // val sb = java.lang.StringBuilder()
  394. // for (b in compressed) {
  395. // sb.append(String.format("%02X ", b))
  396. // }
  397. // val out = sb.toString()
  398. var line = bufferedReader.readLine()
  399. while (line != null) {
  400. stringBuilder.appendLine(line)
  401. line = bufferedReader.readLine()
  402. }
  403. return stringBuilder.toString()
  404. }
  405. fun addShareeToMetadata(
  406. metadataFile: DecryptedFolderMetadataFile,
  407. userId: String,
  408. cert: String,
  409. decryptedMetadataKey: String?
  410. ): DecryptedFolderMetadataFile {
  411. metadataFile.users.add(DecryptedUser(userId, cert, decryptedMetadataKey))
  412. metadataFile.metadata.metadataKey = EncryptionUtils.generateKey()
  413. metadataFile.metadata.keyChecksums.add(hashMetadataKey(metadataFile.metadata.metadataKey))
  414. return metadataFile
  415. }
  416. @Throws(RuntimeException::class)
  417. fun removeShareeFromMetadata(
  418. metadataFile: DecryptedFolderMetadataFile,
  419. userIdToRemove: String
  420. ): DecryptedFolderMetadataFile {
  421. val remove = metadataFile.users.remove(metadataFile.users.find { it.userId == userIdToRemove })
  422. if (!remove) {
  423. throw java.lang.RuntimeException("Removal of user $userIdToRemove failed!")
  424. }
  425. metadataFile.metadata.metadataKey = EncryptionUtils.generateKey()
  426. metadataFile.metadata.keyChecksums.add(hashMetadataKey(metadataFile.metadata.metadataKey))
  427. return metadataFile
  428. }
  429. @Suppress("LongParameterList")
  430. fun addFileToMetadata(
  431. encryptedFileName: String,
  432. ocFile: OCFile,
  433. initializationVector: ByteArray,
  434. authenticationTag: String,
  435. key: ByteArray,
  436. metadataFile: DecryptedFolderMetadataFile,
  437. fileDataStorageManager: FileDataStorageManager
  438. ): DecryptedFolderMetadataFile {
  439. val decryptedFile = DecryptedFile(
  440. ocFile.decryptedFileName,
  441. ocFile.mimeType,
  442. EncryptionUtils.encodeBytesToBase64String(initializationVector),
  443. authenticationTag,
  444. EncryptionUtils.encodeBytesToBase64String(key)
  445. )
  446. metadataFile.metadata.files[encryptedFileName] = decryptedFile
  447. metadataFile.metadata.counter++
  448. ocFile.setE2eCounter(metadataFile.metadata.counter)
  449. fileDataStorageManager.saveFile(ocFile)
  450. return metadataFile
  451. }
  452. fun addFolderToMetadata(
  453. encryptedFileName: String,
  454. fileName: String,
  455. metadataFile: DecryptedFolderMetadataFile,
  456. ocFile: OCFile,
  457. fileDataStorageManager: FileDataStorageManager
  458. ): DecryptedFolderMetadataFile {
  459. metadataFile.metadata.folders[encryptedFileName] = fileName
  460. metadataFile.metadata.counter++
  461. ocFile.setE2eCounter(metadataFile.metadata.counter)
  462. fileDataStorageManager.saveFile(ocFile)
  463. return metadataFile
  464. }
  465. fun removeFolderFromMetadata(
  466. encryptedFileName: String,
  467. metadataFile: DecryptedFolderMetadataFile
  468. ): DecryptedFolderMetadataFile {
  469. metadataFile.metadata.folders.remove(encryptedFileName)
  470. return metadataFile
  471. }
  472. @Throws(IllegalStateException::class)
  473. fun removeFileFromMetadata(fileName: String, metadata: DecryptedFolderMetadataFile) {
  474. metadata.metadata.files.remove(fileName)
  475. ?: throw IllegalStateException("File $fileName not found in metadata!")
  476. }
  477. @Throws(IllegalStateException::class)
  478. fun renameFile(key: String, newName: String, metadataFile: DecryptedFolderMetadataFile) {
  479. if (!metadataFile.metadata.files.containsKey(key)) {
  480. throw IllegalStateException("File with key $key not found in metadata!")
  481. }
  482. metadataFile.metadata.files[key]!!.filename = newName
  483. }
  484. @Throws(UploadException::class, IllegalStateException::class)
  485. fun retrieveMetadata(
  486. folder: OCFile,
  487. client: OwnCloudClient,
  488. user: User,
  489. context: Context
  490. ): Pair<Boolean, DecryptedFolderMetadataFile> {
  491. val getMetadataOperationResult = GetMetadataRemoteOperation(folder.localId).execute(client)
  492. return if (getMetadataOperationResult.isSuccess) {
  493. // decrypt metadata
  494. val metadataResponse = getMetadataOperationResult.resultData
  495. val metadata = parseAnyMetadata(
  496. metadataResponse,
  497. user,
  498. client,
  499. context,
  500. folder
  501. )
  502. Pair(true, metadata)
  503. } else if (getMetadataOperationResult.httpCode == HttpStatus.SC_NOT_FOUND) {
  504. // check parent folder
  505. val parentFolder = FileDataStorageManager(user, context.contentResolver).getFileById(folder.parentId)
  506. ?: throw IllegalStateException("Cannot retrieve metadata!")
  507. val metadata = if (parentFolder.isEncrypted) {
  508. // new metadata but without sharing part
  509. createDecryptedFolderMetadataFile()
  510. } else {
  511. // new metadata
  512. val arbitraryDataProvider: ArbitraryDataProvider = ArbitraryDataProviderImpl(context)
  513. val publicKey: String = arbitraryDataProvider.getValue(user.accountName, EncryptionUtils.PUBLIC_KEY)
  514. createDecryptedFolderMetadataFile().apply {
  515. users = mutableListOf(DecryptedUser(client.userId, publicKey, null))
  516. }
  517. }
  518. Pair(false, metadata)
  519. } else {
  520. // TODO error
  521. throw UploadException("something wrong")
  522. }
  523. }
  524. @Throws(IllegalStateException::class)
  525. @Suppress("TooGenericExceptionCaught")
  526. fun parseAnyMetadata(
  527. metadataResponse: MetadataResponse,
  528. user: User,
  529. client: OwnCloudClient,
  530. context: Context,
  531. folder: OCFile
  532. ): DecryptedFolderMetadataFile {
  533. val arbitraryDataProvider: ArbitraryDataProvider = ArbitraryDataProviderImpl(context)
  534. val privateKey: String = arbitraryDataProvider.getValue(user.accountName, EncryptionUtils.PRIVATE_KEY)
  535. val storageManager = FileDataStorageManager(user, context.contentResolver)
  536. val v2 = EncryptionUtils.deserializeJSON(
  537. metadataResponse.metadata,
  538. object : TypeToken<EncryptedFolderMetadataFile>() {}
  539. )
  540. val decryptedFolderMetadata = if (v2.version == "2.0" || v2.version == "2") {
  541. val userId = AccountManager.get(context).getUserData(
  542. user.toPlatformAccount(),
  543. AccountUtils.Constants.KEY_USER_ID
  544. )
  545. decryptFolderMetadataFile(
  546. v2,
  547. userId,
  548. privateKey,
  549. folder,
  550. storageManager,
  551. client,
  552. folder.e2eCounter,
  553. metadataResponse.signature,
  554. user,
  555. context,
  556. arbitraryDataProvider
  557. )
  558. } else {
  559. // try to deserialize v1
  560. val v1 = EncryptionUtils.deserializeJSON(
  561. metadataResponse.metadata,
  562. object : TypeToken<EncryptedFolderMetadataFileV1?>() {}
  563. )
  564. // decrypt
  565. try {
  566. // decrypt metadata
  567. val decryptedV1 = EncryptionUtils.decryptFolderMetaData(
  568. v1,
  569. privateKey,
  570. arbitraryDataProvider,
  571. user,
  572. folder.localId
  573. )
  574. val publicKey: String = arbitraryDataProvider.getValue(
  575. user.accountName,
  576. EncryptionUtils.PUBLIC_KEY
  577. )
  578. // migrate to v2
  579. migrateV1ToV2andUpload(
  580. decryptedV1,
  581. client.userIdPlain,
  582. publicKey,
  583. folder,
  584. storageManager,
  585. client,
  586. user,
  587. context
  588. )
  589. } catch (e: Exception) {
  590. // TODO do better
  591. throw IllegalStateException("Cannot decrypt metadata")
  592. }
  593. }
  594. // TODO verify metadata
  595. // if (!verifyMetadata(decryptedFolderMetadata)) {
  596. // throw IllegalStateException("Metadata is corrupt!")
  597. // }
  598. return decryptedFolderMetadata
  599. // handle filesDrops
  600. // TODO re-add
  601. // try {
  602. // int filesDropCountBefore = encryptedFolderMetadata.getFiledrop().size();
  603. // DecryptedFolderMetadataFile decryptedFolderMetadata = new EncryptionUtilsV2().decryptFolderMetadataFile(
  604. // encryptedFolderMetadata,
  605. // privateKey);
  606. //
  607. // boolean transferredFiledrop = filesDropCountBefore > 0 && decryptedFolderMetadata.getFiles().size() ==
  608. // encryptedFolderMetadata.getFiles().size() + filesDropCountBefore;
  609. //
  610. // if (transferredFiledrop) {
  611. // // lock folder, only if not already locked
  612. // String token;
  613. // if (existingLockToken == null) {
  614. // token = EncryptionUtils.lockFolder(folder, client);
  615. // } else {
  616. // token = existingLockToken;
  617. // }
  618. //
  619. // // upload metadata
  620. // EncryptedFolderMetadataFile encryptedFolderMetadataNew =
  621. // encryptFolderMetadata(decryptedFolderMetadata, privateKey);
  622. //
  623. // String serializedFolderMetadata = EncryptionUtils.serializeJSON(encryptedFolderMetadataNew);
  624. //
  625. // EncryptionUtils.uploadMetadata(folder,
  626. // serializedFolderMetadata,
  627. // token,
  628. // client,
  629. // true);
  630. //
  631. // // unlock folder, only if not previously locked
  632. // if (existingLockToken == null) {
  633. // RemoteOperationResult unlockFolderResult = EncryptionUtils.unlockFolder(folder, client, token);
  634. //
  635. // if (!unlockFolderResult.isSuccess()) {
  636. // Log_OC.e(TAG, unlockFolderResult.getMessage());
  637. //
  638. // return null;
  639. // }
  640. // }
  641. // }
  642. //
  643. // return decryptedFolderMetadata;
  644. // } catch (Exception e) {
  645. // Log_OC.e(TAG, e.getMessage());
  646. // return null;
  647. // }
  648. // TODO to check
  649. // try {
  650. // int filesDropCountBefore = 0;
  651. // if (encryptedFolderMetadata.getFiledrop() != null) {
  652. // filesDropCountBefore = encryptedFolderMetadata.getFiledrop().size();
  653. // }
  654. // DecryptedFolderMetadataFile decryptedFolderMetadata = EncryptionUtils.decryptFolderMetaData(
  655. // encryptedFolderMetadata,
  656. // privateKey,
  657. // arbitraryDataProvider,
  658. // user,
  659. // folder.getLocalId());
  660. //
  661. // boolean transferredFiledrop = filesDropCountBefore > 0 &&
  662. // decryptedFolderMetadata.getFiles().size() ==
  663. // encryptedFolderMetadata.getFiles().size() + filesDropCountBefore;
  664. //
  665. // if (transferredFiledrop) {
  666. // // lock folder
  667. // String token = EncryptionUtils.lockFolder(folder, client);
  668. //
  669. // // upload metadata
  670. // EncryptedFolderMetadata encryptedFolderMetadataNew =
  671. // encryptFolderMetadata(decryptedFolderMetadata,
  672. // publicKey,
  673. // arbitraryDataProvider,
  674. // user,
  675. // folder.getLocalId());
  676. //
  677. }
  678. @Throws(UploadException::class)
  679. @Suppress("LongParameterList")
  680. fun migrateV1ToV2andUpload(
  681. v1: DecryptedFolderMetadataFileV1,
  682. userId: String,
  683. cert: String,
  684. folder: OCFile,
  685. storageManager: FileDataStorageManager,
  686. client: OwnCloudClient,
  687. user: User,
  688. context: Context
  689. ): DecryptedFolderMetadataFile {
  690. val newMetadata = migrateV1ToV2(
  691. v1,
  692. userId,
  693. cert,
  694. folder,
  695. storageManager
  696. )
  697. // lock
  698. val token = EncryptionUtils.lockFolder(folder, client)
  699. // upload
  700. serializeAndUploadMetadata(
  701. folder,
  702. newMetadata,
  703. token,
  704. client,
  705. true,
  706. context,
  707. user,
  708. storageManager
  709. )
  710. // unlock
  711. EncryptionUtils.unlockFolder(folder, client, token)
  712. return newMetadata
  713. }
  714. @Throws(IllegalStateException::class)
  715. fun migrateV1ToV2(
  716. v1: DecryptedFolderMetadataFileV1,
  717. userId: String,
  718. cert: String,
  719. folder: OCFile,
  720. storageManager: FileDataStorageManager
  721. ): DecryptedFolderMetadataFile {
  722. // key
  723. val key = if (v1.metadata.metadataKeys != null && v1.metadata.metadataKeys.size > 1) {
  724. v1.metadata.metadataKeys[0]
  725. } else {
  726. v1.metadata.metadataKey
  727. }
  728. // create new metadata
  729. val metadataV2 = DecryptedMetadata(
  730. mutableListOf(),
  731. false,
  732. 0,
  733. v1
  734. .files
  735. .filter { it.value.encrypted.mimetype == MimeType.WEBDAV_FOLDER }
  736. .mapValues { it.value.encrypted.filename }
  737. .toMutableMap(),
  738. v1
  739. .files
  740. .filter { it.value.encrypted.mimetype != MimeType.WEBDAV_FOLDER }
  741. .mapValues { migrateDecryptedFileV1ToV2(it.value) }
  742. .toMutableMap(),
  743. EncryptionUtils.decodeStringToBase64Bytes(key) ?: throw IllegalStateException("Metadata key not found!")
  744. )
  745. // upon migration there can only be one user, as there is no sharing yet in place
  746. val users = if (storageManager.getFileById(folder.parentId)?.isEncrypted == false) {
  747. mutableListOf(DecryptedUser(userId, cert, null))
  748. } else {
  749. mutableListOf()
  750. }
  751. // TODO
  752. val filedrop = mutableMapOf<String, DecryptedFile>()
  753. val newMetadata = DecryptedFolderMetadataFile(metadataV2, users, filedrop)
  754. val metadataKey = EncryptionUtils.generateKey() ?: throw UploadException("Could not encrypt folder!")
  755. newMetadata.metadata.metadataKey = metadataKey
  756. newMetadata.metadata.keyChecksums.add(EncryptionUtilsV2().hashMetadataKey(metadataKey))
  757. return newMetadata
  758. }
  759. @VisibleForTesting
  760. fun migrateDecryptedFileV1ToV2(v1: com.owncloud.android.datamodel.e2e.v1.decrypted.DecryptedFile): DecryptedFile {
  761. return DecryptedFile(
  762. v1.encrypted.filename,
  763. v1.encrypted.mimetype,
  764. v1.initializationVector,
  765. v1.authenticationTag ?: "",
  766. v1.encrypted.key
  767. )
  768. }
  769. @Throws(UploadException::class)
  770. @Suppress("LongParameterList")
  771. fun serializeAndUploadMetadata(
  772. folder: OCFile,
  773. metadata: DecryptedFolderMetadataFile,
  774. token: String,
  775. client: OwnCloudClient,
  776. metadataExists: Boolean,
  777. context: Context,
  778. user: User,
  779. storageManager: FileDataStorageManager
  780. ) {
  781. serializeAndUploadMetadata(
  782. folder.remoteId,
  783. metadata,
  784. token,
  785. client,
  786. metadataExists,
  787. context,
  788. user,
  789. folder,
  790. storageManager
  791. )
  792. }
  793. @Throws(UploadException::class)
  794. @Suppress("LongParameterList")
  795. fun serializeAndUploadMetadata(
  796. remoteId: String,
  797. metadata: DecryptedFolderMetadataFile,
  798. token: String,
  799. client: OwnCloudClient,
  800. metadataExists: Boolean,
  801. context: Context,
  802. user: User,
  803. folder: OCFile,
  804. storageManager: FileDataStorageManager
  805. ) {
  806. val arbitraryDataProvider: ArbitraryDataProvider = ArbitraryDataProviderImpl(context)
  807. val privateKeyString: String = arbitraryDataProvider.getValue(user.accountName, EncryptionUtils.PRIVATE_KEY)
  808. val publicKeyString: String = arbitraryDataProvider.getValue(user.accountName, EncryptionUtils.PUBLIC_KEY)
  809. val encryptedFolderMetadata = encryptFolderMetadataFile(
  810. metadata,
  811. client.userId,
  812. folder,
  813. storageManager,
  814. client,
  815. privateKeyString,
  816. user,
  817. context,
  818. arbitraryDataProvider
  819. )
  820. val serializedFolderMetadata = EncryptionUtils.serializeJSON(encryptedFolderMetadata, true)
  821. val cert = EncryptionUtils.convertCertFromString(publicKeyString)
  822. val privateKey = EncryptionUtils.PEMtoPrivateKey(privateKeyString)
  823. val signature = getMessageSignature(cert, privateKey, encryptedFolderMetadata)
  824. val uploadMetadataOperationResult = if (metadataExists) {
  825. // update metadata
  826. UpdateMetadataV2RemoteOperation(
  827. remoteId,
  828. serializedFolderMetadata,
  829. token,
  830. signature
  831. )
  832. .execute(client)
  833. } else {
  834. // store metadata
  835. StoreMetadataV2RemoteOperation(
  836. remoteId,
  837. serializedFolderMetadata,
  838. token,
  839. signature
  840. )
  841. .execute(client)
  842. }
  843. if (!uploadMetadataOperationResult.isSuccess) {
  844. if (metadataExists) {
  845. throw UploadException("Updating metadata was not successful")
  846. } else {
  847. throw UploadException("Storing metadata was not successful")
  848. }
  849. }
  850. }
  851. @Suppress("ReturnCount")
  852. fun verifyMetadata(
  853. encryptedFolderMetadataFile: EncryptedFolderMetadataFile,
  854. decryptedFolderMetadataFile: DecryptedFolderMetadataFile,
  855. oldCounter: Long,
  856. signature: String
  857. ) {
  858. if (decryptedFolderMetadataFile.metadata.counter < oldCounter) {
  859. MainApp.showMessage(R.string.e2e_counter_too_old)
  860. return
  861. }
  862. val message = EncryptionUtils.serializeJSON(encryptedFolderMetadataFile, true)
  863. val certs = decryptedFolderMetadataFile.users.map { EncryptionUtils.convertCertFromString(it.certificate) }
  864. val signedData = getSignedData(signature, message)
  865. if (!verifySignedData(signedData, certs)) {
  866. MainApp.showMessage(R.string.e2e_signature_does_not_match)
  867. return
  868. }
  869. val hashedMetadataKey = hashMetadataKey(decryptedFolderMetadataFile.metadata.metadataKey)
  870. if (!decryptedFolderMetadataFile.metadata.keyChecksums.contains(hashedMetadataKey)) {
  871. MainApp.showMessage(R.string.e2e_hash_not_found)
  872. return
  873. }
  874. }
  875. fun getSignedData(base64encodedSignature: String, message: String): CMSSignedData {
  876. val signature = EncryptionUtils.decodeStringToBase64Bytes(base64encodedSignature)
  877. val asn1Signature = ASN1Sequence.fromByteArray(signature)
  878. val contentInfo = ContentInfo.getInstance(asn1Signature)
  879. val encodedMessage = EncryptionUtils.encodeStringToBase64String(message)
  880. val messageData = encodedMessage.toByteArray()
  881. val cmsProcessableByteArray = CMSProcessableByteArray(messageData)
  882. return CMSSignedData(cmsProcessableByteArray, contentInfo)
  883. }
  884. fun verifySignedData(data: CMSSignedData, certs: List<X509Certificate>): Boolean {
  885. val signer: SignerInformation = data.signerInfos.signers.iterator().next() as SignerInformation
  886. certs.forEach {
  887. try {
  888. if (signer.verify(JcaSimpleSignerInfoVerifierBuilder().build(it))) {
  889. return true
  890. }
  891. } catch (e: java.lang.Exception) {
  892. Log_OC.e(TAG, "Error caught at verifySignedData: $e")
  893. }
  894. }
  895. return false
  896. }
  897. private fun signMessage(cert: X509Certificate, key: PrivateKey, data: ByteArray): CMSSignedData {
  898. val content = CMSProcessableByteArray(data)
  899. val certs = JcaCertStore(listOf(cert))
  900. val sha1signer = JcaContentSignerBuilder("SHA256withRSA").build(key)
  901. val signGen = CMSSignedDataGenerator().apply {
  902. addSignerInfoGenerator(
  903. JcaSignerInfoGeneratorBuilder(JcaDigestCalculatorProviderBuilder().build()).build(
  904. sha1signer,
  905. cert
  906. )
  907. )
  908. addCertificates(certs)
  909. }
  910. return signGen.generate(
  911. content,
  912. false
  913. )
  914. }
  915. /**
  916. * Sign the data with key, embed the certificate associated within the CMSSignedData
  917. * detached data not possible, as to restore asn.1
  918. */
  919. private fun signMessage(
  920. cert: X509Certificate,
  921. key: PrivateKey,
  922. message: EncryptedFolderMetadataFile
  923. ): CMSSignedData {
  924. val json = EncryptionUtils.serializeJSON(message, true)
  925. val base64 = EncryptionUtils.encodeStringToBase64String(json)
  926. val data = base64.toByteArray()
  927. return signMessage(cert, key, data)
  928. }
  929. fun signMessage(cert: X509Certificate, key: PrivateKey, string: String): CMSSignedData {
  930. val base64 = EncryptionUtils.encodeStringToBase64String(string)
  931. val data = base64.toByteArray()
  932. return signMessage(cert, key, data)
  933. }
  934. fun extractSignedString(signedData: CMSSignedData): String {
  935. val ans = signedData.getEncoded("BER")
  936. return EncryptionUtils.encodeBytesToBase64String(ans)
  937. }
  938. fun createDecryptedFolderMetadataFile(): DecryptedFolderMetadataFile {
  939. val metadata = DecryptedMetadata().apply {
  940. keyChecksums.add(hashMetadataKey(metadataKey))
  941. }
  942. return DecryptedFolderMetadataFile(metadata)
  943. }
  944. /**
  945. * SHA-256 hash of metadata-key
  946. */
  947. @Suppress("MagicNumber")
  948. fun hashMetadataKey(metadataKey: ByteArray): String {
  949. val bytes = MessageDigest
  950. .getInstance("SHA-256")
  951. .digest(metadataKey)
  952. return BigInteger(1, bytes).toString(16).padStart(32, '0')
  953. }
  954. fun getMessageSignature(cert: String, privateKey: String, metadataFile: EncryptedFolderMetadataFile): String {
  955. return getMessageSignature(
  956. EncryptionUtils.convertCertFromString(cert),
  957. EncryptionUtils.PEMtoPrivateKey(privateKey),
  958. metadataFile
  959. )
  960. }
  961. private fun getMessageSignature(
  962. cert: X509Certificate,
  963. key: PrivateKey,
  964. message: EncryptedFolderMetadataFile
  965. ): String {
  966. val signedMessage = signMessage(cert, key, message)
  967. return extractSignedString(signedMessage)
  968. }
  969. fun getMessageSignature(cert: X509Certificate, key: PrivateKey, string: String): String {
  970. val signedMessage = signMessage(cert, key, string)
  971. return extractSignedString(signedMessage)
  972. }
  973. companion object {
  974. private val TAG = EncryptionUtils::class.java.simpleName
  975. }
  976. }