BlockDecryptor.swift 3.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. // CryptoSwift
  2. //
  3. // Copyright (C) 2014-2018 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
  4. // This software is provided 'as-is', without any express or implied warranty.
  5. //
  6. // In no event will the authors be held liable for any damages arising from the use of this software.
  7. //
  8. // Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
  9. //
  10. // - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
  11. // - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
  12. // - This notice may not be removed or altered from any source or binary distribution.
  13. //
  14. public class BlockDecryptor: Cryptor, Updatable {
  15. @usableFromInline
  16. let blockSize: Int
  17. @usableFromInline
  18. let padding: Padding
  19. @usableFromInline
  20. var worker: CipherModeWorker
  21. @usableFromInline
  22. var accumulated = Array<UInt8>()
  23. @usableFromInline
  24. init(blockSize: Int, padding: Padding, _ worker: CipherModeWorker) throws {
  25. self.blockSize = blockSize
  26. self.padding = padding
  27. self.worker = worker
  28. }
  29. @inlinable
  30. public func update(withBytes bytes: ArraySlice<UInt8>, isLast: Bool = false) throws -> Array<UInt8> {
  31. self.accumulated += bytes
  32. // If a worker (eg GCM) can combine ciphertext + tag
  33. // we need to remove tag from the ciphertext.
  34. if !isLast && self.accumulated.count < self.blockSize + self.worker.additionalBufferSize {
  35. return []
  36. }
  37. let accumulatedWithoutSuffix: Array<UInt8>
  38. if self.worker.additionalBufferSize > 0 {
  39. // FIXME: how slow is that?
  40. accumulatedWithoutSuffix = Array(self.accumulated.prefix(self.accumulated.count - self.worker.additionalBufferSize))
  41. } else {
  42. accumulatedWithoutSuffix = self.accumulated
  43. }
  44. var processedBytesCount = 0
  45. var plaintext = Array<UInt8>(reserveCapacity: accumulatedWithoutSuffix.count)
  46. // Processing in a block-size manner. It's good for block modes, but bad for stream modes.
  47. for var chunk in accumulatedWithoutSuffix.batched(by: self.blockSize) {
  48. if isLast || (accumulatedWithoutSuffix.count - processedBytesCount) >= blockSize {
  49. let isLastChunk = processedBytesCount + chunk.count == accumulatedWithoutSuffix.count
  50. if isLast, isLastChunk, var finalizingWorker = worker as? FinalizingDecryptModeWorker {
  51. chunk = try finalizingWorker.willDecryptLast(bytes: chunk + accumulated.suffix(worker.additionalBufferSize)) // tag size
  52. }
  53. if !chunk.isEmpty {
  54. plaintext += worker.decrypt(block: chunk)
  55. }
  56. if isLast, isLastChunk, var finalizingWorker = worker as? FinalizingDecryptModeWorker {
  57. plaintext = Array(try finalizingWorker.didDecryptLast(bytes: plaintext.slice))
  58. }
  59. processedBytesCount += chunk.count
  60. }
  61. }
  62. accumulated.removeFirst(processedBytesCount) // super-slow
  63. if isLast {
  64. if accumulatedWithoutSuffix.isEmpty, var finalizingWorker = worker as? FinalizingDecryptModeWorker {
  65. try finalizingWorker.willDecryptLast(bytes: self.accumulated.suffix(self.worker.additionalBufferSize))
  66. plaintext = Array(try finalizingWorker.didDecryptLast(bytes: plaintext.slice))
  67. }
  68. plaintext = self.padding.remove(from: plaintext, blockSize: self.blockSize)
  69. }
  70. return plaintext
  71. }
  72. public func seek(to position: Int) throws {
  73. guard var worker = self.worker as? SeekableModeWorker else {
  74. fatalError("Not supported")
  75. }
  76. try worker.seek(to: position)
  77. self.worker = worker
  78. accumulated = []
  79. }
  80. }