diff --git a/Sources/CryptoSwift/ChaCha20.swift b/Sources/CryptoSwift/ChaCha20.swift index b89323f9..dad144c0 100644 --- a/Sources/CryptoSwift/ChaCha20.swift +++ b/Sources/CryptoSwift/ChaCha20.swift @@ -2,7 +2,7 @@ // CryptoSwift // // Copyright (C) 2014-2017 Marcin Krzyżanowski -// Copyright (C) 2019 Roger Miret +// Copyright (C) 2019 Roger Miret Giné // This software is provided 'as-is', without any express or implied warranty. // // In no event will the authors be held liable for any damages arising from the use of this software. @@ -17,42 +17,9 @@ // https://tools.ietf.org/html/rfc7539 // -public final class ChaCha20: BlockCipher { - public enum Error: Swift.Error { - case invalidKeyOrInitializationVector - case notSupported - } - - public static let blockSize = 64 // 512 / 8 - public let keySize: Int - - fileprivate let key: Key - fileprivate var counter: Array - - public init(key: Array, iv nonce: Array) throws { - precondition(nonce.count == 12 || nonce.count == 8) +public final class ChaCha20: Salsa20 { - if key.count != 32 { - throw Error.invalidKeyOrInitializationVector - } - - self.key = Key(bytes: key) - keySize = self.key.count - - if nonce.count == 8 { - counter = [0, 0, 0, 0, 0, 0, 0, 0] + nonce - } else { - counter = [0, 0, 0, 0] + nonce - } - - assert(counter.count == 16) - } - - fileprivate func rotl(_ a: UInt32, _ b: Int) -> UInt32 { - return (a << b) | (a >> (32 - b)) - } - - fileprivate func qr(_ a: inout UInt32, _ b: inout UInt32, _ c: inout UInt32, _ d: inout UInt32) { + override internal func qr(_ a: inout UInt32, _ b: inout UInt32, _ c: inout UInt32, _ d: inout UInt32) { a = a &+ b d ^= a d = rotl(d, 16) @@ -71,7 +38,7 @@ public final class ChaCha20: BlockCipher { } /// https://tools.ietf.org/html/rfc7539#section-2.3. - fileprivate func core(block: inout Array, counter: Array, key: Array) { + override internal func core(block: inout Array, counter: Array, key: Array) { precondition(block.count == ChaCha20.blockSize) precondition(counter.count == 16) precondition(key.count == 32) @@ -143,142 +110,4 @@ public final class ChaCha20: BlockCipher { block.replaceSubrange(56..<60, with: x14.bigEndian.bytes()) block.replaceSubrange(60..<64, with: x15.bigEndian.bytes()) } - - // XORKeyStream - func process(bytes: ArraySlice, counter: inout Array, key: Array) -> Array { - precondition(counter.count == 16) - precondition(key.count == 32) - - var block = Array(repeating: 0, count: ChaCha20.blockSize) - var bytesSlice = bytes - var out = Array(reserveCapacity: bytesSlice.count) - - while bytesSlice.count >= ChaCha20.blockSize { - core(block: &block, counter: counter, key: key) - for (i, x) in block.enumerated() { - out.append(bytesSlice[bytesSlice.startIndex + i] ^ x) - } - var u: UInt32 = 1 - for i in 0..<4 { - u += UInt32(counter[i]) - counter[i] = UInt8(u & 0xff) - u >>= 8 - } - bytesSlice = bytesSlice[bytesSlice.startIndex + ChaCha20.blockSize.. 0 { - core(block: &block, counter: counter, key: key) - for (i, v) in bytesSlice.enumerated() { - out.append(v ^ block[i]) - } - } - return out - } -} - -// MARK: Cipher - -extension ChaCha20: Cipher { - public func encrypt(_ bytes: ArraySlice) throws -> Array { - return process(bytes: bytes, counter: &counter, key: Array(key)) - } - - public func decrypt(_ bytes: ArraySlice) throws -> Array { - return try encrypt(bytes) - } -} - -// MARK: Encryptor - -extension ChaCha20 { - public struct ChaChaEncryptor: Cryptor, Updatable { - private var accumulated = Array() - private let chacha: ChaCha20 - - init(chacha: ChaCha20) { - self.chacha = chacha - } - - public mutating func update(withBytes bytes: ArraySlice, isLast: Bool = false) throws -> Array { - accumulated += bytes - - var encrypted = Array() - encrypted.reserveCapacity(accumulated.count) - for chunk in accumulated.batched(by: ChaCha20.blockSize) { - if isLast || accumulated.count >= ChaCha20.blockSize { - encrypted += try chacha.encrypt(chunk) - accumulated.removeFirst(chunk.count) // TODO: improve performance - } - } - return encrypted - } - - public func seek(to: Int) throws { - throw Error.notSupported - } - } -} - -// MARK: Decryptor - -extension ChaCha20 { - public struct ChaChaDecryptor: Cryptor, Updatable { - private var accumulated = Array() - - private var offset: Int = 0 - private var offsetToRemove: Int = 0 - private let chacha: ChaCha20 - - init(chacha: ChaCha20) { - self.chacha = chacha - } - - public mutating func update(withBytes bytes: ArraySlice, isLast: Bool = true) throws -> Array { - // prepend "offset" number of bytes at the beginning - if offset > 0 { - accumulated += Array(repeating: 0, count: offset) + bytes - offsetToRemove = offset - offset = 0 - } else { - accumulated += bytes - } - - var plaintext = Array() - plaintext.reserveCapacity(accumulated.count) - for chunk in accumulated.batched(by: ChaCha20.blockSize) { - if isLast || accumulated.count >= ChaCha20.blockSize { - plaintext += try chacha.decrypt(chunk) - - // remove "offset" from the beginning of first chunk - if offsetToRemove > 0 { - plaintext.removeFirst(offsetToRemove) // TODO: improve performance - offsetToRemove = 0 - } - - accumulated.removeFirst(chunk.count) - } - } - - return plaintext - } - - public func seek(to: Int) throws { - throw Error.notSupported - } - } -} - -// MARK: Cryptors - -extension ChaCha20: Cryptors { - //TODO: Use BlockEncryptor/BlockDecryptor - - public func makeEncryptor() -> Cryptor & Updatable { - return ChaCha20.ChaChaEncryptor(chacha: self) - } - - public func makeDecryptor() -> Cryptor & Updatable { - return ChaCha20.ChaChaDecryptor(chacha: self) - } }