22// CryptoSwift
33//
44// Copyright (C) 2014-2017 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
5- // Copyright (C) 2019 Roger Miret <roger.miret@gmail.com>
5+ // Copyright (C) 2019 Roger Miret Giné <roger.miret@gmail.com>
66// This software is provided 'as-is', without any express or implied warranty.
77//
88// In no event will the authors be held liable for any damages arising from the use of this software.
1717// https://tools.ietf.org/html/rfc7539
1818//
1919
20- public final class ChaCha20 : BlockCipher {
21- public enum Error : Swift . Error {
22- case invalidKeyOrInitializationVector
23- case notSupported
24- }
25-
26- public static let blockSize = 64 // 512 / 8
27- public let keySize : Int
28-
29- fileprivate let key : Key
30- fileprivate var counter : Array < UInt8 >
31-
32- public init ( key: Array < UInt8 > , iv nonce: Array < UInt8 > ) throws {
33- precondition ( nonce. count == 12 || nonce. count == 8 )
20+ public final class ChaCha20 : Salsa20 {
3421
35- if key. count != 32 {
36- throw Error . invalidKeyOrInitializationVector
37- }
38-
39- self . key = Key ( bytes: key)
40- keySize = self . key. count
41-
42- if nonce. count == 8 {
43- counter = [ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] + nonce
44- } else {
45- counter = [ 0 , 0 , 0 , 0 ] + nonce
46- }
47-
48- assert ( counter. count == 16 )
49- }
50-
51- fileprivate func rotl( _ a: UInt32 , _ b: Int ) -> UInt32 {
52- return ( a << b) | ( a >> ( 32 - b) )
53- }
54-
55- fileprivate func qr( _ a: inout UInt32 , _ b: inout UInt32 , _ c: inout UInt32 , _ d: inout UInt32 ) {
22+ override internal func qr( _ a: inout UInt32 , _ b: inout UInt32 , _ c: inout UInt32 , _ d: inout UInt32 ) {
5623 a = a &+ b
5724 d ^= a
5825 d = rotl ( d, 16 )
@@ -71,7 +38,7 @@ public final class ChaCha20: BlockCipher {
7138 }
7239
7340 /// https://tools.ietf.org/html/rfc7539#section-2.3.
74- fileprivate func core( block: inout Array < UInt8 > , counter: Array < UInt8 > , key: Array < UInt8 > ) {
41+ override internal func core( block: inout Array < UInt8 > , counter: Array < UInt8 > , key: Array < UInt8 > ) {
7542 precondition ( block. count == ChaCha20 . blockSize)
7643 precondition ( counter. count == 16 )
7744 precondition ( key. count == 32 )
@@ -143,142 +110,4 @@ public final class ChaCha20: BlockCipher {
143110 block. replaceSubrange ( 56 ..< 60 , with: x14. bigEndian. bytes ( ) )
144111 block. replaceSubrange ( 60 ..< 64 , with: x15. bigEndian. bytes ( ) )
145112 }
146-
147- // XORKeyStream
148- func process( bytes: ArraySlice < UInt8 > , counter: inout Array < UInt8 > , key: Array < UInt8 > ) -> Array < UInt8 > {
149- precondition ( counter. count == 16 )
150- precondition ( key. count == 32 )
151-
152- var block = Array < UInt8 > ( repeating: 0 , count: ChaCha20 . blockSize)
153- var bytesSlice = bytes
154- var out = Array < UInt8 > ( reserveCapacity: bytesSlice. count)
155-
156- while bytesSlice. count >= ChaCha20 . blockSize {
157- core ( block: & block, counter: counter, key: key)
158- for (i, x) in block. enumerated ( ) {
159- out. append ( bytesSlice [ bytesSlice. startIndex + i] ^ x)
160- }
161- var u : UInt32 = 1
162- for i in 0 ..< 4 {
163- u += UInt32 ( counter [ i] )
164- counter [ i] = UInt8 ( u & 0xff )
165- u >>= 8
166- }
167- bytesSlice = bytesSlice [ bytesSlice. startIndex + ChaCha20. blockSize..< bytesSlice. endIndex]
168- }
169-
170- if bytesSlice. count > 0 {
171- core ( block: & block, counter: counter, key: key)
172- for (i, v) in bytesSlice. enumerated ( ) {
173- out. append ( v ^ block [ i] )
174- }
175- }
176- return out
177- }
178- }
179-
180- // MARK: Cipher
181-
182- extension ChaCha20 : Cipher {
183- public func encrypt( _ bytes: ArraySlice < UInt8 > ) throws -> Array < UInt8 > {
184- return process ( bytes: bytes, counter: & counter, key: Array ( key) )
185- }
186-
187- public func decrypt( _ bytes: ArraySlice < UInt8 > ) throws -> Array < UInt8 > {
188- return try encrypt ( bytes)
189- }
190- }
191-
192- // MARK: Encryptor
193-
194- extension ChaCha20 {
195- public struct ChaChaEncryptor : Cryptor , Updatable {
196- private var accumulated = Array < UInt8 > ( )
197- private let chacha : ChaCha20
198-
199- init ( chacha: ChaCha20 ) {
200- self . chacha = chacha
201- }
202-
203- public mutating func update( withBytes bytes: ArraySlice < UInt8 > , isLast: Bool = false ) throws -> Array < UInt8 > {
204- accumulated += bytes
205-
206- var encrypted = Array < UInt8 > ( )
207- encrypted. reserveCapacity ( accumulated. count)
208- for chunk in accumulated. batched ( by: ChaCha20 . blockSize) {
209- if isLast || accumulated. count >= ChaCha20 . blockSize {
210- encrypted += try chacha. encrypt ( chunk)
211- accumulated. removeFirst ( chunk. count) // TODO: improve performance
212- }
213- }
214- return encrypted
215- }
216-
217- public func seek( to: Int ) throws {
218- throw Error . notSupported
219- }
220- }
221- }
222-
223- // MARK: Decryptor
224-
225- extension ChaCha20 {
226- public struct ChaChaDecryptor : Cryptor , Updatable {
227- private var accumulated = Array < UInt8 > ( )
228-
229- private var offset : Int = 0
230- private var offsetToRemove : Int = 0
231- private let chacha : ChaCha20
232-
233- init ( chacha: ChaCha20 ) {
234- self . chacha = chacha
235- }
236-
237- public mutating func update( withBytes bytes: ArraySlice < UInt8 > , isLast: Bool = true ) throws -> Array < UInt8 > {
238- // prepend "offset" number of bytes at the beginning
239- if offset > 0 {
240- accumulated += Array < UInt8 > ( repeating: 0 , count: offset) + bytes
241- offsetToRemove = offset
242- offset = 0
243- } else {
244- accumulated += bytes
245- }
246-
247- var plaintext = Array < UInt8 > ( )
248- plaintext. reserveCapacity ( accumulated. count)
249- for chunk in accumulated. batched ( by: ChaCha20 . blockSize) {
250- if isLast || accumulated. count >= ChaCha20 . blockSize {
251- plaintext += try chacha. decrypt ( chunk)
252-
253- // remove "offset" from the beginning of first chunk
254- if offsetToRemove > 0 {
255- plaintext. removeFirst ( offsetToRemove) // TODO: improve performance
256- offsetToRemove = 0
257- }
258-
259- accumulated. removeFirst ( chunk. count)
260- }
261- }
262-
263- return plaintext
264- }
265-
266- public func seek( to: Int ) throws {
267- throw Error . notSupported
268- }
269- }
270- }
271-
272- // MARK: Cryptors
273-
274- extension ChaCha20 : Cryptors {
275- //TODO: Use BlockEncryptor/BlockDecryptor
276-
277- public func makeEncryptor( ) -> Cryptor & Updatable {
278- return ChaCha20 . ChaChaEncryptor ( chacha: self )
279- }
280-
281- public func makeDecryptor( ) -> Cryptor & Updatable {
282- return ChaCha20 . ChaChaDecryptor ( chacha: self )
283- }
284113}
0 commit comments