StreamDecryptor.swift 3.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  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. @usableFromInline
  15. final class StreamDecryptor: Cryptor, Updatable {
  16. @usableFromInline
  17. internal let blockSize: Int
  18. @usableFromInline
  19. internal var worker: CipherModeWorker
  20. @usableFromInline
  21. internal let padding: Padding
  22. @usableFromInline
  23. internal var accumulated = Array<UInt8>()
  24. @usableFromInline
  25. internal var lastBlockRemainder = 0
  26. @usableFromInline
  27. init(blockSize: Int, padding: Padding, _ worker: CipherModeWorker) throws {
  28. self.blockSize = blockSize
  29. self.padding = padding
  30. self.worker = worker
  31. }
  32. // MARK: Updatable
  33. @inlinable
  34. public func update(withBytes bytes: ArraySlice<UInt8>, isLast: Bool) throws -> Array<UInt8> {
  35. self.accumulated += bytes
  36. let toProcess = self.accumulated.prefix(max(self.accumulated.count - self.worker.additionalBufferSize, 0))
  37. if var finalizingWorker = worker as? FinalizingDecryptModeWorker, isLast == true {
  38. // will truncate suffix if needed
  39. try finalizingWorker.willDecryptLast(bytes: self.accumulated.slice)
  40. }
  41. var processedBytesCount = 0
  42. var plaintext = Array<UInt8>(reserveCapacity: bytes.count + self.worker.additionalBufferSize)
  43. for chunk in toProcess.batched(by: self.blockSize) {
  44. plaintext += self.worker.decrypt(block: chunk)
  45. processedBytesCount += chunk.count
  46. }
  47. if var finalizingWorker = worker as? FinalizingDecryptModeWorker, isLast == true {
  48. plaintext = Array(try finalizingWorker.didDecryptLast(bytes: plaintext.slice))
  49. }
  50. // omit unecessary calculation if not needed
  51. if self.padding != .noPadding {
  52. self.lastBlockRemainder = plaintext.count.quotientAndRemainder(dividingBy: self.blockSize).remainder
  53. }
  54. if isLast {
  55. // CTR doesn't need padding. Really. Add padding to the last block if really want. but... don't.
  56. plaintext = self.padding.remove(from: plaintext, blockSize: self.blockSize - self.lastBlockRemainder)
  57. }
  58. self.accumulated.removeFirst(processedBytesCount) // super-slow
  59. if var finalizingWorker = worker as? FinalizingDecryptModeWorker, isLast == true {
  60. plaintext = Array(try finalizingWorker.finalize(decrypt: plaintext.slice))
  61. }
  62. return plaintext
  63. }
  64. @inlinable
  65. public func seek(to position: Int) throws {
  66. guard var worker = self.worker as? SeekableModeWorker else {
  67. fatalError("Not supported")
  68. }
  69. try worker.seek(to: position)
  70. self.worker = worker
  71. }
  72. }