CCM.swift 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. //// CryptoSwift
  2. //
  3. // Copyright (C) 2014-__YEAR__ 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. public struct CCM: StreamMode {
  18. public enum Error: Swift.Error {
  19. /// Invalid IV
  20. case invalidInitializationVector
  21. case invalidParameter
  22. }
  23. public let options: BlockModeOption = [.initializationVectorRequired, .paddingRequired]
  24. private let nonce: Array<UInt8>
  25. private let additionalAuthenticatedData: Array<UInt8>?
  26. private let tagLength: Int
  27. private let messageLength: Int // total message length. need to know in advance
  28. public init(nonce: Array<UInt8>, tagLength: Int, messageLength: Int, additionalAuthenticatedData: Array<UInt8>? = nil) {
  29. self.nonce = nonce
  30. self.tagLength = tagLength
  31. self.additionalAuthenticatedData = additionalAuthenticatedData
  32. self.messageLength = messageLength
  33. }
  34. public func worker(blockSize: Int, cipherOperation: @escaping CipherOperationOnBlock) throws -> CipherModeWorker {
  35. if nonce.isEmpty {
  36. throw Error.invalidInitializationVector
  37. }
  38. return CCMModeWorker(blockSize: blockSize, nonce: nonce.slice, messageLength: messageLength, additionalAuthenticatedData: additionalAuthenticatedData, tagLength: tagLength, cipherOperation: cipherOperation)
  39. }
  40. }
  41. class CCMModeWorker: StreamModeWorker, CounterModeWorker, FinalizingModeWorker {
  42. typealias Counter = Int
  43. var counter = 0
  44. let cipherOperation: CipherOperationOnBlock
  45. let blockSize: Int
  46. private let tagLength: Int
  47. private let messageLength: Int // total message length. need to know in advance
  48. private let q: UInt8
  49. let additionalBufferSize: Int
  50. private let nonce: Array<UInt8>
  51. private var prev: ArraySlice<UInt8> = []
  52. public enum Error: Swift.Error {
  53. case invalidParameter
  54. }
  55. init(blockSize: Int, nonce: ArraySlice<UInt8>, messageLength: Int, additionalAuthenticatedData: [UInt8]?, tagLength: Int, cipherOperation: @escaping CipherOperationOnBlock) {
  56. self.blockSize = blockSize
  57. self.tagLength = tagLength
  58. self.additionalBufferSize = tagLength
  59. self.messageLength = messageLength
  60. self.cipherOperation = cipherOperation
  61. self.nonce = Array(nonce)
  62. self.q = UInt8(15 - nonce.count) // n = 15-q
  63. let hasAssociatedData = additionalAuthenticatedData != nil && !additionalAuthenticatedData!.isEmpty
  64. processControlInformation(nonce: self.nonce, tagLength: tagLength, hasAssociatedData: hasAssociatedData)
  65. if let aad = additionalAuthenticatedData {
  66. process(aad: aad)
  67. }
  68. }
  69. // For the very first time setup new IV (aka y0) from the block0
  70. private func processControlInformation(nonce: [UInt8], tagLength: Int, hasAssociatedData: Bool) {
  71. let block0 = try! format(nonce: nonce, Q: UInt32(messageLength), q: q, t: UInt8(tagLength), hasAssociatedData: hasAssociatedData).slice
  72. let y0 = cipherOperation(block0)!.slice
  73. prev = y0
  74. }
  75. private func process(aad: [UInt8]) {
  76. let encodedAAD = format(aad: aad)
  77. for block_i in encodedAAD.batched(by: 16) {
  78. let y_i = cipherOperation(xor(block_i, prev))!.slice
  79. prev = y_i
  80. counter += 1
  81. }
  82. }
  83. private func S(i: Int) throws -> [UInt8] {
  84. let ctr = try format(counter: i, nonce: nonce, q: q)
  85. return cipherOperation(ctr.slice)!
  86. }
  87. func seek(to position: Int) throws {
  88. self.counter = position
  89. }
  90. func encrypt(block plaintext: ArraySlice<UInt8>) -> Array<UInt8> {
  91. // y[i], where i is the counter
  92. guard let y = cipherOperation(xor(prev, plaintext)),
  93. let S = try? S(i: counter)
  94. else {
  95. return Array(plaintext)
  96. }
  97. let result = xor(plaintext, S) as Array<UInt8> // P xor MSBplen(S)
  98. prev = y.slice
  99. counter += 1
  100. return result
  101. }
  102. // TODO
  103. func decrypt(block ciphertext: ArraySlice<UInt8>) -> Array<UInt8> {
  104. guard let plaintext = cipherOperation(ciphertext) else {
  105. return Array(ciphertext)
  106. }
  107. let result: Array<UInt8> = xor(prev, plaintext)
  108. prev = ciphertext
  109. return result
  110. }
  111. func finalize(encrypt ciphertext: ArraySlice<UInt8>) throws -> Array<UInt8> {
  112. // concatenate T at the end
  113. guard let S0 = try? S(i: 0) else {
  114. return Array(ciphertext)
  115. }
  116. let tag = prev.prefix(tagLength)
  117. return Array(ciphertext) + (xor(tag, S0) as Array<UInt8>)
  118. }
  119. func willDecryptLast(block ciphertext: ArraySlice<UInt8>) throws -> ArraySlice<UInt8> {
  120. return ciphertext
  121. }
  122. func didDecryptLast(block plaintext: ArraySlice<UInt8>) throws -> Array<UInt8> {
  123. return Array(plaintext)
  124. }
  125. }
  126. // Q - octet length of P
  127. // q - octet length of Q. Maximum length (in octets) of payload. An element of {2,3,4,5,6,7,8}
  128. // t - octet length of T (MAC length). An element of {4,6,8,10,12,14,16}
  129. private func format(nonce N: [UInt8], Q: UInt32, q: UInt8, t: UInt8, hasAssociatedData: Bool) throws -> [UInt8] {
  130. var flags0: UInt8 = 0
  131. if hasAssociatedData {
  132. // 7 bit
  133. flags0 |= (1 << 6)
  134. }
  135. // 6,5,4 bit is t in 3 bits
  136. flags0 |= (((t-2)/2) & 0x07) << 3
  137. // 3,2,1 bit is q in 3 bits
  138. flags0 |= ((q-1) & 0x07) << 0
  139. var block0: [UInt8] = Array<UInt8>(repeating: 0, count: 16)
  140. block0[0] = flags0
  141. // N in 1...(15-q) octets, n = 15-q
  142. // n is an element of {7,8,9,10,11,12,13}
  143. let n = 15-Int(q)
  144. guard (n + Int(q)) == 15 else {
  145. // n+q == 15
  146. throw CCMModeWorker.Error.invalidParameter
  147. }
  148. block0[1...n] = N[0...(n-1)]
  149. // Q in (16-q)...15 octets
  150. block0[(16-Int(q))...15] = Q.bytes(totalBytes: Int(q)).slice
  151. return block0
  152. }
  153. /// Formatting of the Counter Blocks. Ctr[i]
  154. /// The counter generation function.
  155. /// Q - octet length of P
  156. /// q - octet length of Q. Maximum length (in octets) of payload. An element of {2,3,4,5,6,7,8}
  157. private func format(counter i: Int, nonce N: [UInt8], q: UInt8) throws -> [UInt8] {
  158. var flags0: UInt8 = 0
  159. // bit 8,7 is Reserved
  160. // bit 4,5,6 shall be set to 0
  161. // 3,2,1 bit is q in 3 bits
  162. flags0 |= ((q-1) & 0x07) << 0
  163. var block = Array<UInt8>(repeating: 0, count: 16) // block[0]
  164. block[0] = flags0
  165. // N in 1...(15-q) octets, n = 15-q
  166. // n is an element of {7,8,9,10,11,12,13}
  167. let n = 15-Int(q)
  168. guard (n + Int(q)) == 15 else {
  169. // n+q == 15
  170. throw CCMModeWorker.Error.invalidParameter
  171. }
  172. block[1...n] = N[0...(n-1)]
  173. // [i]8q in (16-q)...15 octets
  174. block[(16-Int(q))...15] = i.bytes(totalBytes: Int(q)).slice
  175. return block
  176. }
  177. /// Resulting can be partitioned into 16-octet blocks
  178. private func format(aad: [UInt8]) -> [UInt8] {
  179. let a = aad.count
  180. switch Double(a) {
  181. case 0..<65280: // 2^16-2^8
  182. // [a]16
  183. return ZeroPadding().add(to: a.bytes(totalBytes: 2) + aad, blockSize: 16)
  184. case 65280..<4_294_967_296: // 2^32
  185. // [a]32
  186. return ZeroPadding().add(to: [0xFF, 0xFE] + a.bytes(totalBytes: 4) + aad, blockSize: 16)
  187. case 4_294_967_296..<pow(2,64): // 2^64
  188. // [a]64
  189. return ZeroPadding().add(to: [0xFF, 0xFF] + a.bytes(totalBytes: 8) + aad, blockSize: 16)
  190. default:
  191. // Reserved
  192. return ZeroPadding().add(to: aad, blockSize: 16)
  193. }
  194. }