ChaCha20.swift 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. //
  2. // ChaCha20.swift
  3. // CryptoSwift
  4. //
  5. // Created by Marcin Krzyzanowski on 25/08/14.
  6. // Copyright (c) 2014 Marcin Krzyzanowski. All rights reserved.
  7. //
  8. import Foundation
  9. final public class ChaCha20 {
  10. static let blockSize = 64 // 512 / 8
  11. private let stateSize = 16
  12. private var context:Context?
  13. final private class Context {
  14. var input:[UInt32] = [UInt32](count: 16, repeatedValue: 0)
  15. deinit {
  16. for (var i = 0; i < input.count; i++) {
  17. input[i] = 0x00;
  18. }
  19. }
  20. }
  21. public init?(key:[UInt8], iv:[UInt8]) {
  22. if let c = contextSetup(iv: iv, key: key) {
  23. context = c
  24. } else {
  25. return nil
  26. }
  27. }
  28. convenience public init?(key:String, iv:String) {
  29. if let kkey = key.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)?.arrayOfBytes(), let iiv = iv.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)?.arrayOfBytes() {
  30. self.init(key: kkey, iv: iiv)
  31. } else {
  32. self.init(key: [UInt8](), iv: [UInt8]()) //FIXME: this is due Swift bug, remove this line later, when fixed
  33. return nil
  34. }
  35. }
  36. public func encrypt(bytes:[UInt8]) -> [UInt8]? {
  37. if (context == nil) {
  38. return nil
  39. }
  40. return encryptBytes(bytes)
  41. }
  42. public func decrypt(bytes:[UInt8]) -> [UInt8]? {
  43. return encrypt(bytes)
  44. }
  45. private final func wordToByte(input:[UInt32] /* 64 */) -> [UInt8]? /* 16 */ {
  46. if (input.count != stateSize) {
  47. return nil;
  48. }
  49. var x = input
  50. var i = 10
  51. while (i > 0) {
  52. quarterround(&x[0], &x[4], &x[8], &x[12])
  53. quarterround(&x[1], &x[5], &x[9], &x[13])
  54. quarterround(&x[2], &x[6], &x[10], &x[14])
  55. quarterround(&x[3], &x[7], &x[11], &x[15])
  56. quarterround(&x[0], &x[5], &x[10], &x[15])
  57. quarterround(&x[1], &x[6], &x[11], &x[12])
  58. quarterround(&x[2], &x[7], &x[8], &x[13])
  59. quarterround(&x[3], &x[4], &x[9], &x[14])
  60. i -= 2
  61. }
  62. var output = [UInt8]()
  63. output.reserveCapacity(16)
  64. for i in 0..<16 {
  65. x[i] = x[i] &+ input[i]
  66. output += [UInt8((x[i] & 0xFFFFFFFF) >> 24),
  67. UInt8((x[i] & 0xFFFFFF) >> 16),
  68. UInt8((x[i] & 0xFFFF) >> 8),
  69. UInt8((x[i] & 0xFF) >> 0)]
  70. }
  71. return output;
  72. }
  73. private func contextSetup(# iv:[UInt8], key:[UInt8]) -> Context? {
  74. var ctx = Context()
  75. let kbits = key.count * 8
  76. if (kbits != 128 && kbits != 256) {
  77. return nil
  78. }
  79. // 4 - 8
  80. for (var i = 0; i < 4; i++) {
  81. let start = i * 4
  82. ctx.input[i + 4] = wordNumber(key[start..<(start + 4)])
  83. }
  84. var addPos = 0;
  85. switch (kbits) {
  86. case 256:
  87. addPos += 16
  88. // sigma
  89. ctx.input[0] = 0x61707865 //apxe
  90. ctx.input[1] = 0x3320646e //3 dn
  91. ctx.input[2] = 0x79622d32 //yb-2
  92. ctx.input[3] = 0x6b206574 //k et
  93. default:
  94. // tau
  95. ctx.input[0] = 0x61707865 //apxe
  96. ctx.input[1] = 0x3620646e //6 dn
  97. ctx.input[2] = 0x79622d31 //yb-1
  98. ctx.input[3] = 0x6b206574 //k et
  99. break;
  100. }
  101. // 8 - 11
  102. for (var i = 0; i < 4; i++) {
  103. let start = addPos + (i*4)
  104. let bytes = key[start..<(start + 4)]
  105. ctx.input[i + 8] = wordNumber(bytes)
  106. }
  107. // iv
  108. ctx.input[12] = 0
  109. ctx.input[13] = 0
  110. ctx.input[14] = wordNumber(iv[0..<4])
  111. ctx.input[15] = wordNumber(iv[4..<8])
  112. return ctx
  113. }
  114. private final func encryptBytes(message:[UInt8]) -> [UInt8]? {
  115. if let ctx = context {
  116. var c:[UInt8] = [UInt8](count: message.count, repeatedValue: 0)
  117. var cPos:Int = 0
  118. var mPos:Int = 0
  119. var bytes = message.count
  120. while (true) {
  121. if let output = wordToByte(ctx.input) {
  122. ctx.input[12] = ctx.input[12] &+ 1
  123. if (ctx.input[12] == 0) {
  124. ctx.input[13] = ctx.input[13] &+ 1
  125. /* stopping at 2^70 bytes per nonce is user's responsibility */
  126. }
  127. if (bytes <= ChaCha20.blockSize) {
  128. for (var i = 0; i < bytes; i++) {
  129. c[i + cPos] = message[i + mPos] ^ output[i]
  130. }
  131. return c
  132. }
  133. for (var i = 0; i < ChaCha20.blockSize; i++) {
  134. c[i + cPos] = message[i + mPos] ^ output[i]
  135. }
  136. bytes -= ChaCha20.blockSize
  137. cPos += ChaCha20.blockSize
  138. mPos += ChaCha20.blockSize
  139. }
  140. }
  141. }
  142. return nil;
  143. }
  144. private final func quarterround(inout a:UInt32, inout _ b:UInt32, inout _ c:UInt32, inout _ d:UInt32) {
  145. a = a &+ b
  146. d = rotateLeft((d ^ a), 16)
  147. c = c &+ d
  148. b = rotateLeft((b ^ c), 12);
  149. a = a &+ b
  150. d = rotateLeft((d ^ a), 8);
  151. c = c &+ d
  152. b = rotateLeft((b ^ c), 7);
  153. }
  154. }
  155. // MARK: Helpers
  156. /// Change array to number. It's here because arrayOfBytes is too slow
  157. private func wordNumber(bytes:ArraySlice<UInt8>) -> UInt32 {
  158. var value:UInt32 = 0
  159. for (var i:UInt32 = 0, j = 0; i < 4; i++, j++) {
  160. value = value | UInt32(bytes[j]) << (8 * i)
  161. }
  162. return value
  163. }