ChaCha20.swift 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. //
  2. // CryptoSwift
  3. //
  4. // Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
  5. // This software is provided 'as-is', without any express or implied warranty.
  6. //
  7. // In no event will the authors be held liable for any damages arising from the use of this software.
  8. //
  9. // 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:
  10. //
  11. // - 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.
  12. // - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
  13. // - This notice may not be removed or altered from any source or binary distribution.
  14. //
  15. // https://tools.ietf.org/html/rfc7539
  16. //
  17. public final class ChaCha20: BlockCipher {
  18. public enum Error: Swift.Error {
  19. case invalidKeyOrInitializationVector
  20. case notSupported
  21. }
  22. public static let blockSize = 64 // 512 / 8
  23. public let keySize: Int
  24. fileprivate let key: Key
  25. fileprivate var counter: Array<UInt8>
  26. public convenience init(key: Array<UInt8>, iv nonce: Array<UInt8>) throws {
  27. try self.init(key: key, iv: nonce, blockCounter: 0)
  28. }
  29. init(key: Array<UInt8>, iv nonce: Array<UInt8>, blockCounter: UInt32 = 0) throws {
  30. precondition(nonce.count == 12 || nonce.count == 8)
  31. if key.count != 32 {
  32. throw Error.invalidKeyOrInitializationVector
  33. }
  34. self.key = Key(bytes: key)
  35. self.keySize = self.key.count
  36. if nonce.count == 8 {
  37. self.counter = blockCounter.bigEndian.bytes() + [0, 0, 0, 0] + nonce
  38. } else {
  39. self.counter = blockCounter.bigEndian.bytes() + nonce
  40. }
  41. assert(self.counter.count == 16)
  42. }
  43. /// https://tools.ietf.org/html/rfc7539#section-2.3.
  44. fileprivate func core(block: inout Array<UInt8>, counter: Array<UInt8>, key: Array<UInt8>) {
  45. precondition(block.count == ChaCha20.blockSize)
  46. precondition(counter.count == 16)
  47. precondition(key.count == 32)
  48. let j0: UInt32 = 0x61707865
  49. let j1: UInt32 = 0x3320646e // 0x3620646e sigma/tau
  50. let j2: UInt32 = 0x79622d32
  51. let j3: UInt32 = 0x6b206574
  52. let j4: UInt32 = UInt32(bytes: key[0..<4]).bigEndian
  53. let j5: UInt32 = UInt32(bytes: key[4..<8]).bigEndian
  54. let j6: UInt32 = UInt32(bytes: key[8..<12]).bigEndian
  55. let j7: UInt32 = UInt32(bytes: key[12..<16]).bigEndian
  56. let j8: UInt32 = UInt32(bytes: key[16..<20]).bigEndian
  57. let j9: UInt32 = UInt32(bytes: key[20..<24]).bigEndian
  58. let j10: UInt32 = UInt32(bytes: key[24..<28]).bigEndian
  59. let j11: UInt32 = UInt32(bytes: key[28..<32]).bigEndian
  60. let j12: UInt32 = UInt32(bytes: counter[0..<4]).bigEndian
  61. let j13: UInt32 = UInt32(bytes: counter[4..<8]).bigEndian
  62. let j14: UInt32 = UInt32(bytes: counter[8..<12]).bigEndian
  63. let j15: UInt32 = UInt32(bytes: counter[12..<16]).bigEndian
  64. var (x0, x1, x2, x3, x4, x5, x6, x7) = (j0, j1, j2, j3, j4, j5, j6, j7)
  65. var (x8, x9, x10, x11, x12, x13, x14, x15) = (j8, j9, j10, j11, j12, j13, j14, j15)
  66. for _ in 0..<10 { // 20 rounds
  67. x0 = x0 &+ x4
  68. x12 ^= x0
  69. x12 = (x12 << 16) | (x12 >> 16)
  70. x8 = x8 &+ x12
  71. x4 ^= x8
  72. x4 = (x4 << 12) | (x4 >> 20)
  73. x0 = x0 &+ x4
  74. x12 ^= x0
  75. x12 = (x12 << 8) | (x12 >> 24)
  76. x8 = x8 &+ x12
  77. x4 ^= x8
  78. x4 = (x4 << 7) | (x4 >> 25)
  79. x1 = x1 &+ x5
  80. x13 ^= x1
  81. x13 = (x13 << 16) | (x13 >> 16)
  82. x9 = x9 &+ x13
  83. x5 ^= x9
  84. x5 = (x5 << 12) | (x5 >> 20)
  85. x1 = x1 &+ x5
  86. x13 ^= x1
  87. x13 = (x13 << 8) | (x13 >> 24)
  88. x9 = x9 &+ x13
  89. x5 ^= x9
  90. x5 = (x5 << 7) | (x5 >> 25)
  91. x2 = x2 &+ x6
  92. x14 ^= x2
  93. x14 = (x14 << 16) | (x14 >> 16)
  94. x10 = x10 &+ x14
  95. x6 ^= x10
  96. x6 = (x6 << 12) | (x6 >> 20)
  97. x2 = x2 &+ x6
  98. x14 ^= x2
  99. x14 = (x14 << 8) | (x14 >> 24)
  100. x10 = x10 &+ x14
  101. x6 ^= x10
  102. x6 = (x6 << 7) | (x6 >> 25)
  103. x3 = x3 &+ x7
  104. x15 ^= x3
  105. x15 = (x15 << 16) | (x15 >> 16)
  106. x11 = x11 &+ x15
  107. x7 ^= x11
  108. x7 = (x7 << 12) | (x7 >> 20)
  109. x3 = x3 &+ x7
  110. x15 ^= x3
  111. x15 = (x15 << 8) | (x15 >> 24)
  112. x11 = x11 &+ x15
  113. x7 ^= x11
  114. x7 = (x7 << 7) | (x7 >> 25)
  115. x0 = x0 &+ x5
  116. x15 ^= x0
  117. x15 = (x15 << 16) | (x15 >> 16)
  118. x10 = x10 &+ x15
  119. x5 ^= x10
  120. x5 = (x5 << 12) | (x5 >> 20)
  121. x0 = x0 &+ x5
  122. x15 ^= x0
  123. x15 = (x15 << 8) | (x15 >> 24)
  124. x10 = x10 &+ x15
  125. x5 ^= x10
  126. x5 = (x5 << 7) | (x5 >> 25)
  127. x1 = x1 &+ x6
  128. x12 ^= x1
  129. x12 = (x12 << 16) | (x12 >> 16)
  130. x11 = x11 &+ x12
  131. x6 ^= x11
  132. x6 = (x6 << 12) | (x6 >> 20)
  133. x1 = x1 &+ x6
  134. x12 ^= x1
  135. x12 = (x12 << 8) | (x12 >> 24)
  136. x11 = x11 &+ x12
  137. x6 ^= x11
  138. x6 = (x6 << 7) | (x6 >> 25)
  139. x2 = x2 &+ x7
  140. x13 ^= x2
  141. x13 = (x13 << 16) | (x13 >> 16)
  142. x8 = x8 &+ x13
  143. x7 ^= x8
  144. x7 = (x7 << 12) | (x7 >> 20)
  145. x2 = x2 &+ x7
  146. x13 ^= x2
  147. x13 = (x13 << 8) | (x13 >> 24)
  148. x8 = x8 &+ x13
  149. x7 ^= x8
  150. x7 = (x7 << 7) | (x7 >> 25)
  151. x3 = x3 &+ x4
  152. x14 ^= x3
  153. x14 = (x14 << 16) | (x14 >> 16)
  154. x9 = x9 &+ x14
  155. x4 ^= x9
  156. x4 = (x4 << 12) | (x4 >> 20)
  157. x3 = x3 &+ x4
  158. x14 ^= x3
  159. x14 = (x14 << 8) | (x14 >> 24)
  160. x9 = x9 &+ x14
  161. x4 ^= x9
  162. x4 = (x4 << 7) | (x4 >> 25)
  163. }
  164. x0 = x0 &+ j0
  165. x1 = x1 &+ j1
  166. x2 = x2 &+ j2
  167. x3 = x3 &+ j3
  168. x4 = x4 &+ j4
  169. x5 = x5 &+ j5
  170. x6 = x6 &+ j6
  171. x7 = x7 &+ j7
  172. x8 = x8 &+ j8
  173. x9 = x9 &+ j9
  174. x10 = x10 &+ j10
  175. x11 = x11 &+ j11
  176. x12 = x12 &+ j12
  177. x13 = x13 &+ j13
  178. x14 = x14 &+ j14
  179. x15 = x15 &+ j15
  180. block.replaceSubrange(0..<4, with: x0.bigEndian.bytes())
  181. block.replaceSubrange(4..<8, with: x1.bigEndian.bytes())
  182. block.replaceSubrange(8..<12, with: x2.bigEndian.bytes())
  183. block.replaceSubrange(12..<16, with: x3.bigEndian.bytes())
  184. block.replaceSubrange(16..<20, with: x4.bigEndian.bytes())
  185. block.replaceSubrange(20..<24, with: x5.bigEndian.bytes())
  186. block.replaceSubrange(24..<28, with: x6.bigEndian.bytes())
  187. block.replaceSubrange(28..<32, with: x7.bigEndian.bytes())
  188. block.replaceSubrange(32..<36, with: x8.bigEndian.bytes())
  189. block.replaceSubrange(36..<40, with: x9.bigEndian.bytes())
  190. block.replaceSubrange(40..<44, with: x10.bigEndian.bytes())
  191. block.replaceSubrange(44..<48, with: x11.bigEndian.bytes())
  192. block.replaceSubrange(48..<52, with: x12.bigEndian.bytes())
  193. block.replaceSubrange(52..<56, with: x13.bigEndian.bytes())
  194. block.replaceSubrange(56..<60, with: x14.bigEndian.bytes())
  195. block.replaceSubrange(60..<64, with: x15.bigEndian.bytes())
  196. }
  197. // XORKeyStream
  198. func process(bytes: ArraySlice<UInt8>, counter: inout Array<UInt8>, key: Array<UInt8>) -> Array<UInt8> {
  199. precondition(counter.count == 16)
  200. precondition(key.count == 32)
  201. var block = Array<UInt8>(repeating: 0, count: ChaCha20.blockSize)
  202. var bytesSlice = bytes
  203. var out = Array<UInt8>(reserveCapacity: bytesSlice.count)
  204. while bytesSlice.count >= ChaCha20.blockSize {
  205. self.core(block: &block, counter: counter, key: key)
  206. for (i, x) in block.enumerated() {
  207. out.append(bytesSlice[bytesSlice.startIndex + i] ^ x)
  208. }
  209. var u: UInt32 = 1
  210. for i in 0..<4 {
  211. u += UInt32(counter[i])
  212. counter[i] = UInt8(u & 0xff)
  213. u >>= 8
  214. }
  215. bytesSlice = bytesSlice[bytesSlice.startIndex + ChaCha20.blockSize..<bytesSlice.endIndex]
  216. }
  217. if !bytesSlice.isEmpty {
  218. self.core(block: &block, counter: counter, key: key)
  219. for (i, v) in bytesSlice.enumerated() {
  220. out.append(v ^ block[i])
  221. }
  222. }
  223. return out
  224. }
  225. }
  226. // MARK: Cipher
  227. extension ChaCha20: Cipher {
  228. public func encrypt(_ bytes: ArraySlice<UInt8>) throws -> Array<UInt8> {
  229. self.process(bytes: bytes, counter: &self.counter, key: Array(self.key))
  230. }
  231. public func decrypt(_ bytes: ArraySlice<UInt8>) throws -> Array<UInt8> {
  232. try self.encrypt(bytes)
  233. }
  234. }
  235. // MARK: Encryptor
  236. extension ChaCha20 {
  237. public struct ChaChaEncryptor: Cryptor, Updatable {
  238. private var accumulated = Array<UInt8>()
  239. private let chacha: ChaCha20
  240. init(chacha: ChaCha20) {
  241. self.chacha = chacha
  242. }
  243. public mutating func update(withBytes bytes: ArraySlice<UInt8>, isLast: Bool = false) throws -> Array<UInt8> {
  244. self.accumulated += bytes
  245. var encrypted = Array<UInt8>()
  246. encrypted.reserveCapacity(self.accumulated.count)
  247. for chunk in self.accumulated.batched(by: ChaCha20.blockSize) {
  248. if isLast || self.accumulated.count >= ChaCha20.blockSize {
  249. encrypted += try self.chacha.encrypt(chunk)
  250. self.accumulated.removeFirst(chunk.count) // TODO: improve performance
  251. }
  252. }
  253. return encrypted
  254. }
  255. public func seek(to: Int) throws {
  256. throw Error.notSupported
  257. }
  258. }
  259. }
  260. // MARK: Decryptor
  261. extension ChaCha20 {
  262. public struct ChaChaDecryptor: Cryptor, Updatable {
  263. private var accumulated = Array<UInt8>()
  264. private var offset: Int = 0
  265. private var offsetToRemove: Int = 0
  266. private let chacha: ChaCha20
  267. init(chacha: ChaCha20) {
  268. self.chacha = chacha
  269. }
  270. public mutating func update(withBytes bytes: ArraySlice<UInt8>, isLast: Bool = true) throws -> Array<UInt8> {
  271. // prepend "offset" number of bytes at the beginning
  272. if self.offset > 0 {
  273. self.accumulated += Array<UInt8>(repeating: 0, count: self.offset) + bytes
  274. self.offsetToRemove = self.offset
  275. self.offset = 0
  276. } else {
  277. self.accumulated += bytes
  278. }
  279. var plaintext = Array<UInt8>()
  280. plaintext.reserveCapacity(self.accumulated.count)
  281. for chunk in self.accumulated.batched(by: ChaCha20.blockSize) {
  282. if isLast || self.accumulated.count >= ChaCha20.blockSize {
  283. plaintext += try self.chacha.decrypt(chunk)
  284. // remove "offset" from the beginning of first chunk
  285. if self.offsetToRemove > 0 {
  286. plaintext.removeFirst(self.offsetToRemove) // TODO: improve performance
  287. self.offsetToRemove = 0
  288. }
  289. self.accumulated.removeFirst(chunk.count)
  290. }
  291. }
  292. return plaintext
  293. }
  294. public func seek(to: Int) throws {
  295. throw Error.notSupported
  296. }
  297. }
  298. }
  299. // MARK: Cryptors
  300. extension ChaCha20: Cryptors {
  301. //TODO: Use BlockEncryptor/BlockDecryptor
  302. public func makeEncryptor() -> Cryptor & Updatable {
  303. ChaCha20.ChaChaEncryptor(chacha: self)
  304. }
  305. public func makeDecryptor() -> Cryptor & Updatable {
  306. ChaCha20.ChaChaDecryptor(chacha: self)
  307. }
  308. }