CCM.swift 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  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. // CCM mode combines the well known CBC-MAC with the well known counter mode of encryption.
  15. // https://tools.ietf.org/html/rfc3610
  16. // https://csrc.nist.gov/publications/detail/sp/800-38c/final
  17. #if canImport(Darwin)
  18. import Darwin
  19. #else
  20. import Glibc
  21. #endif
  22. /// Counter with Cipher Block Chaining-Message Authentication Code
  23. public struct CCM: StreamMode {
  24. public enum Error: Swift.Error {
  25. /// Invalid IV
  26. case invalidInitializationVector
  27. case invalidParameter
  28. case fail
  29. }
  30. public let options: BlockModeOption = [.initializationVectorRequired, .useEncryptToDecrypt]
  31. private let nonce: Array<UInt8>
  32. private let additionalAuthenticatedData: Array<UInt8>?
  33. private let tagLength: Int
  34. private let messageLength: Int // total message length. need to know in advance
  35. // `authenticationTag` nil for encryption, known tag for decryption
  36. /// For encryption, the value is set at the end of the encryption.
  37. /// For decryption, this is a known Tag to validate against.
  38. public var authenticationTag: Array<UInt8>?
  39. /// Initialize CCM
  40. ///
  41. /// - Parameters:
  42. /// - iv: Initialization vector. Nonce. Valid length between 7 and 13 bytes.
  43. /// - tagLength: Authentication tag length, in bytes. Value of {4, 6, 8, 10, 12, 14, 16}.
  44. /// - messageLength: Plaintext message length (excluding tag if attached). Length have to be provided in advance.
  45. /// - additionalAuthenticatedData: Additional authenticated data.
  46. public init(iv: Array<UInt8>, tagLength: Int, messageLength: Int, additionalAuthenticatedData: Array<UInt8>? = nil) {
  47. self.nonce = iv
  48. self.tagLength = tagLength
  49. self.additionalAuthenticatedData = additionalAuthenticatedData
  50. self.messageLength = messageLength // - tagLength
  51. }
  52. /// Initialize CCM
  53. ///
  54. /// - Parameters:
  55. /// - iv: Initialization vector. Nonce. Valid length between 7 and 13 bytes.
  56. /// - tagLength: Authentication tag length, in bytes. Value of {4, 6, 8, 10, 12, 14, 16}.
  57. /// - messageLength: Plaintext message length (excluding tag if attached). Length have to be provided in advance.
  58. /// - authenticationTag: Authentication Tag value if not concatenated to ciphertext.
  59. /// - additionalAuthenticatedData: Additional authenticated data.
  60. public init(iv: Array<UInt8>, tagLength: Int, messageLength: Int, authenticationTag: Array<UInt8>, additionalAuthenticatedData: Array<UInt8>? = nil) {
  61. self.init(iv: iv, tagLength: tagLength, messageLength: messageLength, additionalAuthenticatedData: additionalAuthenticatedData)
  62. self.authenticationTag = authenticationTag
  63. }
  64. public func worker(blockSize: Int, cipherOperation: @escaping CipherOperationOnBlock) throws -> CipherModeWorker {
  65. if self.nonce.isEmpty {
  66. throw Error.invalidInitializationVector
  67. }
  68. return CCMModeWorker(blockSize: blockSize, nonce: self.nonce.slice, messageLength: self.messageLength, additionalAuthenticatedData: self.additionalAuthenticatedData, tagLength: self.tagLength, cipherOperation: cipherOperation)
  69. }
  70. }
  71. class CCMModeWorker: StreamModeWorker, SeekableModeWorker, CounterModeWorker, FinalizingEncryptModeWorker, FinalizingDecryptModeWorker {
  72. typealias Counter = Int
  73. var counter = 0
  74. let cipherOperation: CipherOperationOnBlock
  75. let blockSize: Int
  76. private let tagLength: Int
  77. private let messageLength: Int // total message length. need to know in advance
  78. private let q: UInt8
  79. let additionalBufferSize: Int
  80. private var keystreamPosIdx = 0
  81. private let nonce: Array<UInt8>
  82. private var last_y: ArraySlice<UInt8> = []
  83. private var keystream: Array<UInt8> = []
  84. // Known Tag used to validate during decryption
  85. private var expectedTag: Array<UInt8>?
  86. public enum Error: Swift.Error {
  87. case invalidParameter
  88. }
  89. init(blockSize: Int, nonce: ArraySlice<UInt8>, messageLength: Int, additionalAuthenticatedData: [UInt8]?, expectedTag: Array<UInt8>? = nil, tagLength: Int, cipherOperation: @escaping CipherOperationOnBlock) {
  90. self.blockSize = 16 // CCM is defined for 128 block size
  91. self.tagLength = tagLength
  92. self.additionalBufferSize = tagLength
  93. self.messageLength = messageLength
  94. self.expectedTag = expectedTag
  95. self.cipherOperation = cipherOperation
  96. self.nonce = Array(nonce)
  97. self.q = UInt8(15 - nonce.count) // n = 15-q
  98. let hasAssociatedData = additionalAuthenticatedData != nil && !additionalAuthenticatedData!.isEmpty
  99. self.processControlInformation(nonce: self.nonce, tagLength: tagLength, hasAssociatedData: hasAssociatedData)
  100. if let aad = additionalAuthenticatedData, hasAssociatedData {
  101. self.process(aad: aad)
  102. }
  103. }
  104. // For the very first time setup new IV (aka y0) from the block0
  105. private func processControlInformation(nonce: [UInt8], tagLength: Int, hasAssociatedData: Bool) {
  106. let block0 = try! format(nonce: nonce, Q: UInt32(self.messageLength), q: self.q, t: UInt8(tagLength), hasAssociatedData: hasAssociatedData).slice
  107. let y0 = self.cipherOperation(block0)!.slice
  108. self.last_y = y0
  109. }
  110. private func process(aad: [UInt8]) {
  111. let encodedAAD = format(aad: aad)
  112. for block_i in encodedAAD.batched(by: 16) {
  113. let y_i = self.cipherOperation(xor(block_i, self.last_y))!.slice
  114. self.last_y = y_i
  115. }
  116. }
  117. private func S(i: Int) throws -> [UInt8] {
  118. let ctr = try format(counter: i, nonce: nonce, q: q)
  119. return self.cipherOperation(ctr.slice)!
  120. }
  121. func seek(to position: Int) throws {
  122. self.counter = position
  123. self.keystream = try self.S(i: position)
  124. let offset = position % self.blockSize
  125. self.keystreamPosIdx = offset
  126. }
  127. func encrypt(block plaintext: ArraySlice<UInt8>) -> Array<UInt8> {
  128. var result = Array<UInt8>(reserveCapacity: plaintext.count)
  129. var processed = 0
  130. while processed < plaintext.count {
  131. // Need a full block here to update keystream and do CBC
  132. if self.keystream.isEmpty || self.keystreamPosIdx == self.blockSize {
  133. // y[i], where i is the counter. Can encrypt 1 block at a time
  134. self.counter += 1
  135. guard let S = try? S(i: counter) else { return Array(plaintext) }
  136. let plaintextP = addPadding(Array(plaintext), blockSize: blockSize)
  137. guard let y = cipherOperation(xor(last_y, plaintextP)) else { return Array(plaintext) }
  138. self.last_y = y.slice
  139. self.keystream = S
  140. self.keystreamPosIdx = 0
  141. }
  142. let xored: Array<UInt8> = xor(plaintext[plaintext.startIndex.advanced(by: processed)...], keystream[keystreamPosIdx...])
  143. keystreamPosIdx += xored.count
  144. processed += xored.count
  145. result += xored
  146. }
  147. return result
  148. }
  149. func finalize(encrypt ciphertext: ArraySlice<UInt8>) throws -> ArraySlice<UInt8> {
  150. // concatenate T at the end
  151. guard let S0 = try? S(i: 0) else { return ciphertext }
  152. let computedTag = xor(last_y.prefix(self.tagLength), S0) as ArraySlice<UInt8>
  153. return ciphertext + computedTag
  154. }
  155. // Decryption is stream
  156. // CBC is block
  157. private var accumulatedPlaintext: [UInt8] = []
  158. func decrypt(block ciphertext: ArraySlice<UInt8>) -> Array<UInt8> {
  159. var output = Array<UInt8>(reserveCapacity: ciphertext.count)
  160. do {
  161. var currentCounter = self.counter
  162. var processed = 0
  163. while processed < ciphertext.count {
  164. // Need a full block here to update keystream and do CBC
  165. // New keystream for a new block
  166. if self.keystream.isEmpty || self.keystreamPosIdx == self.blockSize {
  167. currentCounter += 1
  168. guard let S = try? S(i: currentCounter) else { return Array(ciphertext) }
  169. self.keystream = S
  170. self.keystreamPosIdx = 0
  171. }
  172. let xored: Array<UInt8> = xor(ciphertext[ciphertext.startIndex.advanced(by: processed)...], keystream[keystreamPosIdx...]) // plaintext
  173. keystreamPosIdx += xored.count
  174. processed += xored.count
  175. output += xored
  176. self.counter = currentCounter
  177. }
  178. }
  179. // Accumulate plaintext for the MAC calculations at the end.
  180. // It would be good to process it together though, here.
  181. self.accumulatedPlaintext += output
  182. // Shouldn't return plaintext until validate tag.
  183. // With incremental update, can't validate tag until all block are processed.
  184. return output
  185. }
  186. func finalize(decrypt plaintext: ArraySlice<UInt8>) throws -> ArraySlice<UInt8> {
  187. // concatenate T at the end
  188. let computedTag = Array(last_y.prefix(self.tagLength))
  189. guard let expectedTag = self.expectedTag, expectedTag == computedTag else {
  190. throw CCM.Error.fail
  191. }
  192. return plaintext
  193. }
  194. @discardableResult
  195. func willDecryptLast(bytes ciphertext: ArraySlice<UInt8>) throws -> ArraySlice<UInt8> {
  196. // get tag of additionalBufferSize size
  197. // `ciphertext` contains at least additionalBufferSize bytes
  198. // overwrite expectedTag property used later for verification
  199. guard let S0 = try? S(i: 0) else { return ciphertext }
  200. self.expectedTag = xor(ciphertext.suffix(self.tagLength), S0) as [UInt8]
  201. return ciphertext[ciphertext.startIndex..<ciphertext.endIndex.advanced(by: -Swift.min(tagLength, ciphertext.count))]
  202. }
  203. func didDecryptLast(bytes plaintext: ArraySlice<UInt8>) throws -> ArraySlice<UInt8> {
  204. // Calculate Tag, from the last CBC block, for accumulated plaintext.
  205. var processed = 0
  206. for block in self.accumulatedPlaintext.batched(by: self.blockSize) {
  207. let blockP = addPadding(Array(block), blockSize: blockSize)
  208. guard let y = cipherOperation(xor(last_y, blockP)) else { return plaintext }
  209. self.last_y = y.slice
  210. processed += block.count
  211. }
  212. self.accumulatedPlaintext.removeFirst(processed)
  213. return plaintext
  214. }
  215. }
  216. // Q - octet length of P
  217. // q - octet length of Q. Maximum length (in octets) of payload. An element of {2,3,4,5,6,7,8}
  218. // t - octet length of T (MAC length). An element of {4,6,8,10,12,14,16}
  219. private func format(nonce N: [UInt8], Q: UInt32, q: UInt8, t: UInt8, hasAssociatedData: Bool) throws -> [UInt8] {
  220. var flags0: UInt8 = 0
  221. if hasAssociatedData {
  222. // 7 bit
  223. flags0 |= (1 << 6)
  224. }
  225. // 6,5,4 bit is t in 3 bits
  226. flags0 |= (((t - 2) / 2) & 0x07) << 3
  227. // 3,2,1 bit is q in 3 bits
  228. flags0 |= ((q - 1) & 0x07) << 0
  229. var block0: [UInt8] = Array<UInt8>(repeating: 0, count: 16)
  230. block0[0] = flags0
  231. // N in 1...(15-q) octets, n = 15-q
  232. // n is an element of {7,8,9,10,11,12,13}
  233. let n = 15 - Int(q)
  234. guard (n + Int(q)) == 15 else {
  235. // n+q == 15
  236. throw CCMModeWorker.Error.invalidParameter
  237. }
  238. block0[1...n] = N[0...(n - 1)]
  239. // Q in (16-q)...15 octets
  240. block0[(16 - Int(q))...15] = Q.bytes(totalBytes: Int(q)).slice
  241. return block0
  242. }
  243. /// Formatting of the Counter Blocks. Ctr[i]
  244. /// The counter generation function.
  245. /// Q - octet length of P
  246. /// q - octet length of Q. Maximum length (in octets) of payload. An element of {2,3,4,5,6,7,8}
  247. private func format(counter i: Int, nonce N: [UInt8], q: UInt8) throws -> [UInt8] {
  248. var flags0: UInt8 = 0
  249. // bit 8,7 is Reserved
  250. // bit 4,5,6 shall be set to 0
  251. // 3,2,1 bit is q in 3 bits
  252. flags0 |= ((q - 1) & 0x07) << 0
  253. var block = Array<UInt8>(repeating: 0, count: 16) // block[0]
  254. block[0] = flags0
  255. // N in 1...(15-q) octets, n = 15-q
  256. // n is an element of {7,8,9,10,11,12,13}
  257. let n = 15 - Int(q)
  258. guard (n + Int(q)) == 15 else {
  259. // n+q == 15
  260. throw CCMModeWorker.Error.invalidParameter
  261. }
  262. block[1...n] = N[0...(n - 1)]
  263. // [i]8q in (16-q)...15 octets
  264. block[(16 - Int(q))...15] = i.bytes(totalBytes: Int(q)).slice
  265. return block
  266. }
  267. /// Resulting can be partitioned into 16-octet blocks
  268. private func format(aad: [UInt8]) -> [UInt8] {
  269. let a = aad.count
  270. switch Double(a) {
  271. case 0..<65280: // 2^16-2^8
  272. // [a]16
  273. return addPadding(a.bytes(totalBytes: 2) + aad, blockSize: 16)
  274. case 65280..<4_294_967_296: // 2^32
  275. // [a]32
  276. return addPadding([0xFF, 0xFE] + a.bytes(totalBytes: 4) + aad, blockSize: 16)
  277. case 4_294_967_296..<pow(2, 64): // 2^64
  278. // [a]64
  279. return addPadding([0xFF, 0xFF] + a.bytes(totalBytes: 8) + aad, blockSize: 16)
  280. default:
  281. // Reserved
  282. return addPadding(aad, blockSize: 16)
  283. }
  284. }
  285. // If data is not a multiple of block size bytes long then the remainder is zero padded
  286. // Note: It's similar to ZeroPadding, but it's not the same.
  287. private func addPadding(_ bytes: Array<UInt8>, blockSize: Int) -> Array<UInt8> {
  288. if bytes.isEmpty {
  289. return Array<UInt8>(repeating: 0, count: blockSize)
  290. }
  291. let remainder = bytes.count % blockSize
  292. if remainder == 0 {
  293. return bytes
  294. }
  295. let paddingCount = blockSize - remainder
  296. if paddingCount > 0 {
  297. return bytes + Array<UInt8>(repeating: 0, count: paddingCount)
  298. }
  299. return bytes
  300. }