1 |
efrain |
1 |
//
|
|
|
2 |
// RNCryptor.swift
|
|
|
3 |
//
|
|
|
4 |
// Copyright © 2015 Rob Napier. All rights reserved.
|
|
|
5 |
//
|
|
|
6 |
// This code is licensed under the MIT License:
|
|
|
7 |
//
|
|
|
8 |
// Permission is hereby granted, free of charge, to any person obtaining a
|
|
|
9 |
// copy of this software and associated documentation files (the "Software"),
|
|
|
10 |
// to deal in the Software without restriction, including without limitation
|
|
|
11 |
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
|
12 |
// and/or sell copies of the Software, and to permit persons to whom the
|
|
|
13 |
// Software is furnished to do so, subject to the following conditions:
|
|
|
14 |
//
|
|
|
15 |
// The above copyright notice and this permission notice shall be included in
|
|
|
16 |
// all copies or substantial portions of the Software.
|
|
|
17 |
//
|
|
|
18 |
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
19 |
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
20 |
// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
21 |
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
22 |
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
|
23 |
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
|
24 |
// DEALINGS IN THE SOFTWARE.
|
|
|
25 |
//
|
|
|
26 |
|
|
|
27 |
import Foundation
|
|
|
28 |
import CommonCrypto
|
|
|
29 |
|
|
|
30 |
|
|
|
31 |
/// The `RNCryptorType` protocol defines generic API to a mutable,
|
|
|
32 |
/// incremental, password-based encryptor or decryptor. Its generic
|
|
|
33 |
/// usage is as follows:
|
|
|
34 |
///
|
|
|
35 |
/// let cryptor = Encryptor(password: "mypassword")
|
|
|
36 |
/// // or Decryptor()
|
|
|
37 |
///
|
|
|
38 |
/// var result = Data()
|
|
|
39 |
/// for data in datas {
|
|
|
40 |
/// result.appendData(try cryptor.update(data))
|
|
|
41 |
/// }
|
|
|
42 |
/// result.appendData(try cryptor.final())
|
|
|
43 |
///
|
|
|
44 |
/// After calling `finalData()`, the cryptor is no longer valid.
|
|
|
45 |
public protocol RNCryptorType {
|
|
|
46 |
|
|
|
47 |
/// Creates and returns a cryptor.
|
|
|
48 |
///
|
|
|
49 |
/// - parameter password: Non-empty password string. This will be interpretted as UTF-8.
|
|
|
50 |
init(password: String)
|
|
|
51 |
|
|
|
52 |
/// Updates cryptor with data and returns processed data.
|
|
|
53 |
///
|
|
|
54 |
/// - parameter data: Data to process. May be empty.
|
|
|
55 |
/// - throws: `Error`
|
|
|
56 |
/// - returns: Processed data. May be empty.
|
|
|
57 |
func update(withData data: Data) throws -> Data
|
|
|
58 |
|
|
|
59 |
/// Returns trailing data and invalidates the cryptor.
|
|
|
60 |
///
|
|
|
61 |
/// - throws: `Error`
|
|
|
62 |
/// - returns: Trailing data
|
|
|
63 |
func finalData() throws -> Data
|
|
|
64 |
}
|
|
|
65 |
|
|
|
66 |
public extension RNCryptorType {
|
|
|
67 |
/// Simplified, generic interface to `RNCryptorType`. Takes a data,
|
|
|
68 |
/// returns a processed data. Generally you should use
|
|
|
69 |
/// `RNCryptor.encrypt(data:withPassword:)`, or
|
|
|
70 |
/// `RNCryptor.decrypt(data:withPassword:)` instead, but this is useful
|
|
|
71 |
/// for code that is neutral on whether it is encrypting or decrypting.
|
|
|
72 |
///
|
|
|
73 |
/// - throws: `Error`
|
|
|
74 |
fileprivate func oneshot(data: Data) throws -> Data {
|
|
|
75 |
var result = try update(withData: data)
|
|
|
76 |
result.append(try finalData())
|
|
|
77 |
return result
|
|
|
78 |
}
|
|
|
79 |
}
|
|
|
80 |
|
|
|
81 |
/// RNCryptor encryption/decryption interface.
|
|
|
82 |
public enum RNCryptor {
|
|
|
83 |
|
|
|
84 |
/// Errors thrown by `RNCryptorType`.
|
|
|
85 |
public enum Error: Int, Swift.Error {
|
|
|
86 |
/// Ciphertext was corrupt or password was incorrect.
|
|
|
87 |
/// It is not possible to distinguish between these cases in the v3 data format.
|
|
|
88 |
case hmacMismatch = 1
|
|
|
89 |
|
|
|
90 |
/// Unrecognized data format. Usually this means the data is corrupt.
|
|
|
91 |
case unknownHeader = 2
|
|
|
92 |
|
|
|
93 |
/// `final()` was called before sufficient data was passed to `update(withData:)`
|
|
|
94 |
case messageTooShort
|
|
|
95 |
|
|
|
96 |
/// Memory allocation failure. This should never happen.
|
|
|
97 |
case memoryFailure
|
|
|
98 |
|
|
|
99 |
/// A password-based decryptor was used on a key-based ciphertext, or vice-versa.
|
|
|
100 |
case invalidCredentialType
|
|
|
101 |
}
|
|
|
102 |
|
|
|
103 |
/// Encrypt data using password and return encrypted data.
|
|
|
104 |
public static func encrypt(data: Data, withPassword password: String) -> Data {
|
|
|
105 |
return Encryptor(password: password).encrypt(data: data)
|
|
|
106 |
}
|
|
|
107 |
|
|
|
108 |
/// Decrypt data using password and return decrypted data. Throws if
|
|
|
109 |
/// password is incorrect or ciphertext is in the wrong format.
|
|
|
110 |
/// - throws `Error`
|
|
|
111 |
public static func decrypt(data: Data, withPassword password: String) throws -> Data {
|
|
|
112 |
return try Decryptor(password: password).decrypt(data: data)
|
|
|
113 |
}
|
|
|
114 |
|
|
|
115 |
/// Generates random Data of given length
|
|
|
116 |
/// Crashes if `length` is larger than allocatable memory, or if the system random number generator is not available.
|
|
|
117 |
public static func randomData(ofLength length: Int) -> Data {
|
|
|
118 |
var data = Data(count: length)
|
|
|
119 |
let result = data.withUnsafeMutableBytes { SecRandomCopyBytes(kSecRandomDefault, length, $0.baseAddress!) }
|
|
|
120 |
guard result == errSecSuccess else {
|
|
|
121 |
fatalError("SECURITY FAILURE: Could not generate secure random numbers: \(result).")
|
|
|
122 |
}
|
|
|
123 |
return data
|
|
|
124 |
}
|
|
|
125 |
|
|
|
126 |
/// A encryptor for the latest data format. If compatibility with other RNCryptor
|
|
|
127 |
/// implementations is required, you may wish to use the specific encryptor version rather
|
|
|
128 |
/// than accepting "latest."
|
|
|
129 |
///
|
|
|
130 |
public final class Encryptor: RNCryptorType {
|
|
|
131 |
private let encryptor: EncryptorV3
|
|
|
132 |
|
|
|
133 |
/// Creates and returns a cryptor.
|
|
|
134 |
///
|
|
|
135 |
/// - parameter password: Non-empty password string. This will be interpretted as UTF-8.
|
|
|
136 |
public init(password: String) {
|
|
|
137 |
precondition(password != "")
|
|
|
138 |
encryptor = EncryptorV3(password: password)
|
|
|
139 |
}
|
|
|
140 |
|
|
|
141 |
/// Updates cryptor with data and returns processed data.
|
|
|
142 |
///
|
|
|
143 |
/// - parameter data: Data to process. May be empty.
|
|
|
144 |
/// - returns: Processed data. May be empty.
|
|
|
145 |
public func update(withData data: Data) -> Data {
|
|
|
146 |
return encryptor.update(withData: data)
|
|
|
147 |
}
|
|
|
148 |
|
|
|
149 |
/// Returns trailing data and invalidates the cryptor.
|
|
|
150 |
///
|
|
|
151 |
/// - returns: Trailing data
|
|
|
152 |
public func finalData() -> Data {
|
|
|
153 |
return encryptor.finalData()
|
|
|
154 |
}
|
|
|
155 |
|
|
|
156 |
/// Simplified, generic interface to `RNCryptorType`. Takes a data,
|
|
|
157 |
/// returns a processed data, and invalidates the cryptor.
|
|
|
158 |
public func encrypt(data: Data) -> Data {
|
|
|
159 |
return encryptor.encrypt(data: data)
|
|
|
160 |
}
|
|
|
161 |
}
|
|
|
162 |
|
|
|
163 |
/// Password-based decryptor that can handle any supported format.
|
|
|
164 |
public final class Decryptor : RNCryptorType {
|
|
|
165 |
private var decryptors: [VersionedDecryptorType.Type] = [DecryptorV3.self]
|
|
|
166 |
|
|
|
167 |
private var buffer = Data()
|
|
|
168 |
private var decryptor: RNCryptorType?
|
|
|
169 |
private let password: String
|
|
|
170 |
|
|
|
171 |
/// Creates and returns a cryptor.
|
|
|
172 |
///
|
|
|
173 |
/// - parameter password: Non-empty password string. This will be interpretted as UTF-8.
|
|
|
174 |
public init(password: String) {
|
|
|
175 |
assert(password != "")
|
|
|
176 |
self.password = password
|
|
|
177 |
}
|
|
|
178 |
|
|
|
179 |
/// Decrypt data using password and return decrypted data, invalidating decryptor. Throws if
|
|
|
180 |
/// password is incorrect or ciphertext is in the wrong format.
|
|
|
181 |
/// - throws `Error`
|
|
|
182 |
public func decrypt(data: Data) throws -> Data {
|
|
|
183 |
return try oneshot(data: data)
|
|
|
184 |
}
|
|
|
185 |
|
|
|
186 |
/// Updates cryptor with data and returns processed data.
|
|
|
187 |
///
|
|
|
188 |
/// - parameter data: Data to process. May be empty.
|
|
|
189 |
/// - throws: `Error`
|
|
|
190 |
/// - returns: Processed data. May be empty.
|
|
|
191 |
public func update(withData data: Data) throws -> Data {
|
|
|
192 |
if let d = decryptor {
|
|
|
193 |
return try d.update(withData: data)
|
|
|
194 |
}
|
|
|
195 |
|
|
|
196 |
buffer.append(data)
|
|
|
197 |
|
|
|
198 |
let toCheck:[VersionedDecryptorType.Type]
|
|
|
199 |
(toCheck, decryptors) = decryptors.splitPassFail { self.buffer.count >= $0.preambleSize }
|
|
|
200 |
|
|
|
201 |
for decryptorType in toCheck {
|
|
|
202 |
if decryptorType.canDecrypt(preamble: buffer.subdata(in: 0..<decryptorType.preambleSize)) {
|
|
|
203 |
let d = decryptorType.init(password: password)
|
|
|
204 |
decryptor = d
|
|
|
205 |
let result = try d.update(withData: buffer)
|
|
|
206 |
buffer.count = 0
|
|
|
207 |
return result
|
|
|
208 |
}
|
|
|
209 |
}
|
|
|
210 |
|
|
|
211 |
guard !decryptors.isEmpty else { throw Error.unknownHeader }
|
|
|
212 |
return Data()
|
|
|
213 |
}
|
|
|
214 |
|
|
|
215 |
/// Returns trailing data and invalidates the cryptor.
|
|
|
216 |
///
|
|
|
217 |
/// - throws: `Error`
|
|
|
218 |
/// - returns: Trailing data
|
|
|
219 |
public func finalData() throws -> Data {
|
|
|
220 |
guard let d = decryptor else {
|
|
|
221 |
throw Error.unknownHeader
|
|
|
222 |
}
|
|
|
223 |
return try d.finalData()
|
|
|
224 |
}
|
|
|
225 |
}
|
|
|
226 |
}
|
|
|
227 |
|
|
|
228 |
// V3 implementaion
|
|
|
229 |
public extension RNCryptor {
|
|
|
230 |
/// V3 format settings
|
|
|
231 |
final class FormatV3 {
|
|
|
232 |
/// Size of AES and HMAC keys
|
|
|
233 |
public static let keySize = kCCKeySizeAES256
|
|
|
234 |
|
|
|
235 |
/// Size of PBKDF2 salt
|
|
|
236 |
public static let saltSize = 8
|
|
|
237 |
|
|
|
238 |
/// Generate a key from a password and salt
|
|
|
239 |
/// - parameters:
|
|
|
240 |
/// - password: Password to convert
|
|
|
241 |
/// - salt: Salt. Generally constructed with RNCryptor.randomDataOfLength(FormatV3.saltSize)
|
|
|
242 |
/// - returns: Key of length FormatV3.keySize
|
|
|
243 |
public static func makeKey(forPassword password: String, withSalt salt: Data) -> Data {
|
|
|
244 |
|
|
|
245 |
let passwordArray = password.utf8.map(Int8.init)
|
|
|
246 |
let saltArray = Array(salt)
|
|
|
247 |
|
|
|
248 |
var derivedKey = Array<UInt8>(repeating: 0, count: keySize)
|
|
|
249 |
|
|
|
250 |
// All the crazy casting because CommonCryptor hates Swift
|
|
|
251 |
let algorithm = CCPBKDFAlgorithm(kCCPBKDF2)
|
|
|
252 |
let prf = CCPseudoRandomAlgorithm(kCCPRFHmacAlgSHA1)
|
|
|
253 |
let pbkdf2Rounds = UInt32(10000)
|
|
|
254 |
|
|
|
255 |
let result = CCCryptorStatus(
|
|
|
256 |
CCKeyDerivationPBKDF(
|
|
|
257 |
algorithm,
|
|
|
258 |
passwordArray, passwordArray.count,
|
|
|
259 |
saltArray, saltArray.count,
|
|
|
260 |
prf, pbkdf2Rounds,
|
|
|
261 |
&derivedKey, keySize)
|
|
|
262 |
)
|
|
|
263 |
guard result == CCCryptorStatus(kCCSuccess) else {
|
|
|
264 |
fatalError("SECURITY FAILURE: Could not derive secure password (\(result))")
|
|
|
265 |
}
|
|
|
266 |
return Data(derivedKey)
|
|
|
267 |
}
|
|
|
268 |
|
|
|
269 |
static let formatVersion = UInt8(3)
|
|
|
270 |
static let ivSize = kCCBlockSizeAES128
|
|
|
271 |
static let hmacSize = Int(CC_SHA256_DIGEST_LENGTH)
|
|
|
272 |
static let keyHeaderSize = 1 + 1 + kCCBlockSizeAES128
|
|
|
273 |
static let passwordHeaderSize = 1 + 1 + 8 + 8 + kCCBlockSizeAES128
|
|
|
274 |
}
|
|
|
275 |
|
|
|
276 |
/// Format version 3 encryptor. Use this to ensure a specific format verison
|
|
|
277 |
/// or when using keys (which are inherrently versions-specific). To use
|
|
|
278 |
/// "the latest encryptor" with a password, use `Encryptor` instead.
|
|
|
279 |
final class EncryptorV3 : RNCryptorType {
|
|
|
280 |
private let engine: Engine
|
|
|
281 |
private let hmac: HMACV3
|
|
|
282 |
private var pendingHeader: Data?
|
|
|
283 |
|
|
|
284 |
/// Creates and returns an encryptor.
|
|
|
285 |
///
|
|
|
286 |
/// - parameter password: Non-empty password string. This will be interpretted as UTF-8.
|
|
|
287 |
public convenience init(password: String) {
|
|
|
288 |
self.init(
|
|
|
289 |
password: password,
|
|
|
290 |
encryptionSalt: RNCryptor.randomData(ofLength: V3.saltSize),
|
|
|
291 |
hmacSalt: RNCryptor.randomData(ofLength: V3.saltSize),
|
|
|
292 |
iv: RNCryptor.randomData(ofLength: V3.ivSize))
|
|
|
293 |
}
|
|
|
294 |
|
|
|
295 |
/// Creates and returns an encryptor using keys.
|
|
|
296 |
///
|
|
|
297 |
/// - Attention: This method requires some expertise to use correctly.
|
|
|
298 |
/// Most users should use `init(password:)` which is simpler
|
|
|
299 |
/// to use securely.
|
|
|
300 |
///
|
|
|
301 |
/// Keys should not be generated directly from strings (`.dataUsingEncoding()` or similar).
|
|
|
302 |
/// Ideally, keys should be random (`Cryptor.randomDataOfLength()` or some other high-quality
|
|
|
303 |
/// random generator. If keys must be generated from strings, then use `FormatV3.keyForPassword(salt:)`
|
|
|
304 |
/// with a random salt, or just use password-based encryption (that's what it's for).
|
|
|
305 |
///
|
|
|
306 |
/// - parameters:
|
|
|
307 |
/// - encryptionKey: AES-256 key. Must be exactly FormatV3.keySize (kCCKeySizeAES256, 32 bytes)
|
|
|
308 |
/// - hmacKey: HMAC key. Must be exactly FormatV3.keySize (kCCKeySizeAES256, 32 bytes)
|
|
|
309 |
public convenience init(encryptionKey: Data, hmacKey: Data) {
|
|
|
310 |
self.init(encryptionKey: encryptionKey, hmacKey: hmacKey, iv: RNCryptor.randomData(ofLength: V3.ivSize))
|
|
|
311 |
}
|
|
|
312 |
|
|
|
313 |
/// Takes a data, returns a processed data, and invalidates the cryptor.
|
|
|
314 |
public func encrypt(data: Data) -> Data {
|
|
|
315 |
return try! oneshot(data: data)
|
|
|
316 |
}
|
|
|
317 |
|
|
|
318 |
/// Updates cryptor with data and returns encrypted data.
|
|
|
319 |
///
|
|
|
320 |
/// - parameter data: Data to process. May be empty.
|
|
|
321 |
/// - returns: Processed data. May be empty.
|
|
|
322 |
public func update(withData data: Data) -> Data {
|
|
|
323 |
// It should not be possible for this to fail during encryption
|
|
|
324 |
return handle(data: engine.update(withData: data))
|
|
|
325 |
}
|
|
|
326 |
|
|
|
327 |
/// Returns trailing data and invalidates the cryptor.
|
|
|
328 |
///
|
|
|
329 |
/// - returns: Trailing data
|
|
|
330 |
public func finalData() -> Data {
|
|
|
331 |
var result = handle(data: engine.finalData())
|
|
|
332 |
result.append(hmac.finalData())
|
|
|
333 |
return result
|
|
|
334 |
}
|
|
|
335 |
|
|
|
336 |
// Expose random numbers for testing
|
|
|
337 |
internal convenience init(encryptionKey: Data, hmacKey: Data, iv: Data) {
|
|
|
338 |
let preamble = [V3.formatVersion, UInt8(0)]
|
|
|
339 |
var header = Data(preamble)
|
|
|
340 |
header.append(iv)
|
|
|
341 |
self.init(encryptionKey: encryptionKey, hmacKey: hmacKey, iv: iv, header: header)
|
|
|
342 |
}
|
|
|
343 |
|
|
|
344 |
// Expose random numbers for testing
|
|
|
345 |
internal convenience init(password: String, encryptionSalt: Data, hmacSalt: Data, iv: Data) {
|
|
|
346 |
let encryptionKey = V3.makeKey(forPassword: password, withSalt: encryptionSalt)
|
|
|
347 |
let hmacKey = V3.makeKey(forPassword: password, withSalt: hmacSalt)
|
|
|
348 |
|
|
|
349 |
let preamble = [V3.formatVersion, UInt8(1)]
|
|
|
350 |
var header = Data(preamble)
|
|
|
351 |
header.append(encryptionSalt)
|
|
|
352 |
header.append(hmacSalt)
|
|
|
353 |
header.append(iv)
|
|
|
354 |
|
|
|
355 |
self.init(encryptionKey: encryptionKey, hmacKey: hmacKey, iv: iv, header: header)
|
|
|
356 |
}
|
|
|
357 |
|
|
|
358 |
private init(encryptionKey: Data, hmacKey: Data, iv: Data, header: Data) {
|
|
|
359 |
precondition(encryptionKey.count == V3.keySize)
|
|
|
360 |
precondition(hmacKey.count == V3.keySize)
|
|
|
361 |
precondition(iv.count == V3.ivSize)
|
|
|
362 |
hmac = HMACV3(key: hmacKey)
|
|
|
363 |
engine = Engine(operation: .encrypt, key: encryptionKey, iv: iv)
|
|
|
364 |
pendingHeader = header
|
|
|
365 |
}
|
|
|
366 |
|
|
|
367 |
private func handle(data: Data) -> Data {
|
|
|
368 |
let result: Data
|
|
|
369 |
if var accum = pendingHeader {
|
|
|
370 |
pendingHeader = nil
|
|
|
371 |
accum.append(data)
|
|
|
372 |
result = accum
|
|
|
373 |
} else {
|
|
|
374 |
result = data
|
|
|
375 |
}
|
|
|
376 |
hmac.update(withData: result)
|
|
|
377 |
return result
|
|
|
378 |
}
|
|
|
379 |
}
|
|
|
380 |
|
|
|
381 |
/// Format version 3 decryptor. This is required in order to decrypt
|
|
|
382 |
/// using keys (since key configuration is version-specific). For password
|
|
|
383 |
/// decryption, `Decryptor` is generally preferred, and will call this
|
|
|
384 |
/// if appropriate.
|
|
|
385 |
final class DecryptorV3: VersionedDecryptorType {
|
|
|
386 |
//
|
|
|
387 |
// Static methods
|
|
|
388 |
//
|
|
|
389 |
fileprivate static let preambleSize = 1
|
|
|
390 |
fileprivate static func canDecrypt(preamble: Data) -> Bool {
|
|
|
391 |
assert(preamble.count >= 1)
|
|
|
392 |
return preamble[0] == 3
|
|
|
393 |
}
|
|
|
394 |
|
|
|
395 |
//
|
|
|
396 |
// Private properties
|
|
|
397 |
//
|
|
|
398 |
private var buffer = Data()
|
|
|
399 |
private var decryptorEngine: DecryptorEngineV3?
|
|
|
400 |
private let credential: Credential
|
|
|
401 |
|
|
|
402 |
|
|
|
403 |
/// Creates and returns a decryptor.
|
|
|
404 |
///
|
|
|
405 |
/// - parameter password: Non-empty password string. This will be interpretted as UTF-8.
|
|
|
406 |
public init(password: String) {
|
|
|
407 |
credential = .password(password)
|
|
|
408 |
}
|
|
|
409 |
|
|
|
410 |
/// Creates and returns a decryptor using keys.
|
|
|
411 |
///
|
|
|
412 |
/// - parameters:
|
|
|
413 |
/// - encryptionKey: AES-256 key. Must be exactly FormatV3.keySize (kCCKeySizeAES256, 32 bytes)
|
|
|
414 |
/// - hmacKey: HMAC key. Must be exactly FormatV3.keySize (kCCKeySizeAES256, 32 bytes)
|
|
|
415 |
public init(encryptionKey: Data, hmacKey: Data) {
|
|
|
416 |
precondition(encryptionKey.count == V3.keySize)
|
|
|
417 |
precondition(hmacKey.count == V3.hmacSize)
|
|
|
418 |
credential = .keys(encryptionKey: encryptionKey, hmacKey: hmacKey)
|
|
|
419 |
}
|
|
|
420 |
|
|
|
421 |
/// Decrypt data using password and return decrypted data. Throws if
|
|
|
422 |
/// password is incorrect or ciphertext is in the wrong format.
|
|
|
423 |
/// - throws `Error`
|
|
|
424 |
public func decrypt(data: Data) throws -> Data {
|
|
|
425 |
return try oneshot(data: data)
|
|
|
426 |
}
|
|
|
427 |
|
|
|
428 |
/// Updates cryptor with data and returns encrypted data.
|
|
|
429 |
///
|
|
|
430 |
/// - parameter data: Data to process. May be empty.
|
|
|
431 |
/// - returns: Processed data. May be empty.
|
|
|
432 |
public func update(withData data: Data) throws -> Data {
|
|
|
433 |
if let e = decryptorEngine {
|
|
|
434 |
return e.update(withData: data)
|
|
|
435 |
}
|
|
|
436 |
|
|
|
437 |
buffer.append(data)
|
|
|
438 |
guard buffer.count >= requiredHeaderSize else {
|
|
|
439 |
return Data()
|
|
|
440 |
}
|
|
|
441 |
|
|
|
442 |
let e = try makeEngine(credential: credential, header: buffer.subdata(in: 0..<requiredHeaderSize))
|
|
|
443 |
decryptorEngine = e
|
|
|
444 |
let body = buffer.subdata(in: requiredHeaderSize..<buffer.count)
|
|
|
445 |
buffer.count = 0
|
|
|
446 |
return e.update(withData: body)
|
|
|
447 |
}
|
|
|
448 |
|
|
|
449 |
/// Returns trailing data and invalidates the cryptor.
|
|
|
450 |
///
|
|
|
451 |
/// - returns: Trailing data
|
|
|
452 |
public func finalData() throws -> Data {
|
|
|
453 |
guard let result = try decryptorEngine?.finalData() else {
|
|
|
454 |
throw Error.messageTooShort
|
|
|
455 |
}
|
|
|
456 |
return result
|
|
|
457 |
}
|
|
|
458 |
|
|
|
459 |
//
|
|
|
460 |
// Private functions
|
|
|
461 |
//
|
|
|
462 |
|
|
|
463 |
private var requiredHeaderSize: Int {
|
|
|
464 |
switch credential {
|
|
|
465 |
case .password: return V3.passwordHeaderSize
|
|
|
466 |
case .keys: return V3.keyHeaderSize
|
|
|
467 |
}
|
|
|
468 |
}
|
|
|
469 |
|
|
|
470 |
private func makeEngine(credential: Credential, header: Data) throws -> DecryptorEngineV3 {
|
|
|
471 |
switch credential {
|
|
|
472 |
case let .password(password):
|
|
|
473 |
return try makeEngine(password: password, header: header)
|
|
|
474 |
case let .keys(encryptionKey, hmacKey):
|
|
|
475 |
return try makeEngine(encryptionKey: encryptionKey, hmacKey: hmacKey, header: header)
|
|
|
476 |
}
|
|
|
477 |
}
|
|
|
478 |
|
|
|
479 |
private func makeEngine(password: String, header: Data) throws -> DecryptorEngineV3 {
|
|
|
480 |
assert(password != "")
|
|
|
481 |
precondition(header.count == V3.passwordHeaderSize)
|
|
|
482 |
|
|
|
483 |
guard DecryptorV3.canDecrypt(preamble: header) else {
|
|
|
484 |
throw Error.unknownHeader
|
|
|
485 |
}
|
|
|
486 |
|
|
|
487 |
guard header[1] == 1 else {
|
|
|
488 |
throw Error.invalidCredentialType
|
|
|
489 |
}
|
|
|
490 |
|
|
|
491 |
let encryptionSalt = header.subdata(in: Range(2...9))
|
|
|
492 |
let hmacSalt = header.subdata(in: Range(10...17))
|
|
|
493 |
let iv = header.subdata(in: Range(18...33))
|
|
|
494 |
|
|
|
495 |
let encryptionKey = V3.makeKey(forPassword: password, withSalt: encryptionSalt)
|
|
|
496 |
let hmacKey = V3.makeKey(forPassword: password, withSalt: hmacSalt)
|
|
|
497 |
|
|
|
498 |
return DecryptorEngineV3(encryptionKey: encryptionKey, hmacKey: hmacKey, iv: iv, header: header)
|
|
|
499 |
}
|
|
|
500 |
|
|
|
501 |
private func makeEngine(encryptionKey: Data, hmacKey: Data, header: Data) throws -> DecryptorEngineV3 {
|
|
|
502 |
precondition(header.count == V3.keyHeaderSize)
|
|
|
503 |
precondition(encryptionKey.count == V3.keySize)
|
|
|
504 |
precondition(hmacKey.count == V3.keySize)
|
|
|
505 |
|
|
|
506 |
guard DecryptorV3.canDecrypt(preamble: header) else {
|
|
|
507 |
throw Error.unknownHeader
|
|
|
508 |
}
|
|
|
509 |
|
|
|
510 |
guard header[1] == 0 else {
|
|
|
511 |
throw Error.invalidCredentialType
|
|
|
512 |
}
|
|
|
513 |
|
|
|
514 |
let iv = header.subdata(in: 2..<18)
|
|
|
515 |
return DecryptorEngineV3(encryptionKey: encryptionKey, hmacKey: hmacKey, iv: iv, header: header)
|
|
|
516 |
}
|
|
|
517 |
}
|
|
|
518 |
}
|
|
|
519 |
|
|
|
520 |
internal enum CryptorOperation: CCOperation {
|
|
|
521 |
case encrypt = 0 // CCOperation(kCCEncrypt)
|
|
|
522 |
case decrypt = 1 // CCOperation(kCCDecrypt)
|
|
|
523 |
}
|
|
|
524 |
|
|
|
525 |
internal final class Engine {
|
|
|
526 |
private let cryptor: CCCryptorRef?
|
|
|
527 |
private var buffer = Data()
|
|
|
528 |
|
|
|
529 |
init(operation: CryptorOperation, key: Data, iv: Data) {
|
|
|
530 |
|
|
|
531 |
cryptor = key.withUnsafeBytes { (keyPtr) in
|
|
|
532 |
iv.withUnsafeBytes { (ivPtr) in
|
|
|
533 |
|
|
|
534 |
var cryptorOut: CCCryptorRef?
|
|
|
535 |
let result = CCCryptorCreate(
|
|
|
536 |
operation.rawValue,
|
|
|
537 |
CCAlgorithm(kCCAlgorithmAES128), CCOptions(kCCOptionPKCS7Padding),
|
|
|
538 |
keyPtr.baseAddress!, keyPtr.count,
|
|
|
539 |
ivPtr.baseAddress!,
|
|
|
540 |
&cryptorOut
|
|
|
541 |
)
|
|
|
542 |
|
|
|
543 |
// It is a programming error to create us with illegal values
|
|
|
544 |
// This is an internal class, so we can constrain what is sent to us.
|
|
|
545 |
// If this is ever made public, it should throw instead of asserting.
|
|
|
546 |
assert(result == CCCryptorStatus(kCCSuccess))
|
|
|
547 |
return cryptorOut
|
|
|
548 |
}
|
|
|
549 |
}
|
|
|
550 |
}
|
|
|
551 |
|
|
|
552 |
deinit {
|
|
|
553 |
if cryptor != nil {
|
|
|
554 |
CCCryptorRelease(cryptor)
|
|
|
555 |
}
|
|
|
556 |
}
|
|
|
557 |
|
|
|
558 |
func sizeBuffer(forDataLength length: Int) -> Int {
|
|
|
559 |
let size = CCCryptorGetOutputLength(cryptor, length, true)
|
|
|
560 |
buffer.count = size
|
|
|
561 |
return size
|
|
|
562 |
}
|
|
|
563 |
|
|
|
564 |
func update(withData data: Data) -> Data {
|
|
|
565 |
let outputLength = sizeBuffer(forDataLength: data.count)
|
|
|
566 |
var dataOutMoved = 0
|
|
|
567 |
|
|
|
568 |
let result = data.withUnsafeBytes { dataPtr in
|
|
|
569 |
buffer.withUnsafeMutableBytes { bufferPtr in
|
|
|
570 |
return CCCryptorUpdate(
|
|
|
571 |
cryptor,
|
|
|
572 |
dataPtr.baseAddress!, dataPtr.count,
|
|
|
573 |
bufferPtr.baseAddress!, outputLength,
|
|
|
574 |
&dataOutMoved)
|
|
|
575 |
}
|
|
|
576 |
}
|
|
|
577 |
|
|
|
578 |
// The only error returned by CCCryptorUpdate is kCCBufferTooSmall, which would be a programming error
|
|
|
579 |
assert(result == CCCryptorStatus(kCCSuccess), "RNCRYPTOR BUG. PLEASE REPORT. (\(result)")
|
|
|
580 |
|
|
|
581 |
buffer.count = dataOutMoved
|
|
|
582 |
return buffer
|
|
|
583 |
}
|
|
|
584 |
|
|
|
585 |
func finalData() -> Data {
|
|
|
586 |
let outputLength = sizeBuffer(forDataLength: 0)
|
|
|
587 |
var dataOutMoved = 0
|
|
|
588 |
|
|
|
589 |
let result = buffer.withUnsafeMutableBytes {
|
|
|
590 |
CCCryptorFinal(
|
|
|
591 |
cryptor,
|
|
|
592 |
$0.baseAddress!, outputLength,
|
|
|
593 |
&dataOutMoved
|
|
|
594 |
)
|
|
|
595 |
}
|
|
|
596 |
|
|
|
597 |
// Note that since iOS 6, CCryptor will never return padding errors or other decode errors.
|
|
|
598 |
// I'm not aware of any non-catastrophic (MemoryAllocation) situation in which this
|
|
|
599 |
// can fail. Using assert() just in case, but we'll ignore errors in Release.
|
|
|
600 |
// https://devforums.apple.com/message/920802#920802
|
|
|
601 |
assert(result == CCCryptorStatus(kCCSuccess), "RNCRYPTOR BUG. PLEASE REPORT. (\(result)")
|
|
|
602 |
|
|
|
603 |
buffer.count = dataOutMoved
|
|
|
604 |
defer { buffer = Data() }
|
|
|
605 |
return buffer
|
|
|
606 |
}
|
|
|
607 |
}
|
|
|
608 |
|
|
|
609 |
internal typealias V3 = RNCryptor.FormatV3
|
|
|
610 |
|
|
|
611 |
private enum Credential {
|
|
|
612 |
case password(String)
|
|
|
613 |
case keys(encryptionKey: Data, hmacKey: Data)
|
|
|
614 |
}
|
|
|
615 |
|
|
|
616 |
private final class DecryptorEngineV3 {
|
|
|
617 |
private let buffer = OverflowingBuffer(capacity: V3.hmacSize)
|
|
|
618 |
private let hmac: HMACV3
|
|
|
619 |
private let engine: Engine
|
|
|
620 |
|
|
|
621 |
init(encryptionKey: Data, hmacKey: Data, iv: Data, header: Data) {
|
|
|
622 |
precondition(encryptionKey.count == V3.keySize)
|
|
|
623 |
precondition(hmacKey.count == V3.hmacSize)
|
|
|
624 |
precondition(iv.count == V3.ivSize)
|
|
|
625 |
|
|
|
626 |
hmac = HMACV3(key: hmacKey)
|
|
|
627 |
hmac.update(withData: header)
|
|
|
628 |
engine = Engine(operation: .decrypt, key: encryptionKey, iv: iv)
|
|
|
629 |
}
|
|
|
630 |
|
|
|
631 |
func update(withData data: Data) -> Data {
|
|
|
632 |
let overflow = buffer.update(withData: data)
|
|
|
633 |
hmac.update(withData: overflow)
|
|
|
634 |
return engine.update(withData: overflow)
|
|
|
635 |
}
|
|
|
636 |
|
|
|
637 |
func finalData() throws -> Data {
|
|
|
638 |
let hash = hmac.finalData()
|
|
|
639 |
if !isEqualInConsistentTime(trusted: hash, untrusted: buffer.finalData()) {
|
|
|
640 |
throw RNCryptor.Error.hmacMismatch
|
|
|
641 |
}
|
|
|
642 |
return engine.finalData()
|
|
|
643 |
}
|
|
|
644 |
}
|
|
|
645 |
|
|
|
646 |
private final class HMACV3 {
|
|
|
647 |
var context = CCHmacContext()
|
|
|
648 |
|
|
|
649 |
init(key: Data) {
|
|
|
650 |
key.withUnsafeBytes {
|
|
|
651 |
CCHmacInit(
|
|
|
652 |
&context,
|
|
|
653 |
CCHmacAlgorithm(kCCHmacAlgSHA256),
|
|
|
654 |
$0.baseAddress!,
|
|
|
655 |
key.count
|
|
|
656 |
)
|
|
|
657 |
}
|
|
|
658 |
}
|
|
|
659 |
|
|
|
660 |
func update(withData data: Data) {
|
|
|
661 |
data.withUnsafeBytes { CCHmacUpdate(&context, $0.baseAddress!, data.count) }
|
|
|
662 |
}
|
|
|
663 |
|
|
|
664 |
func finalData() -> Data {
|
|
|
665 |
var hmac = Data(count: V3.hmacSize)
|
|
|
666 |
hmac.withUnsafeMutableBytes { CCHmacFinal(&context, $0.baseAddress!) }
|
|
|
667 |
return hmac
|
|
|
668 |
}
|
|
|
669 |
}
|
|
|
670 |
|
|
|
671 |
// Internal protocol for version-specific decryptors.
|
|
|
672 |
private protocol VersionedDecryptorType: RNCryptorType {
|
|
|
673 |
static var preambleSize: Int { get }
|
|
|
674 |
static func canDecrypt(preamble: Data) -> Bool
|
|
|
675 |
init(password: String)
|
|
|
676 |
}
|
|
|
677 |
|
|
|
678 |
private extension Collection {
|
|
|
679 |
// Split collection into ([pass], [fail]) based on predicate.
|
|
|
680 |
func splitPassFail(forPredicate predicate: (Iterator.Element) -> Bool) -> ([Iterator.Element], [Iterator.Element]) {
|
|
|
681 |
var pass: [Iterator.Element] = []
|
|
|
682 |
var fail: [Iterator.Element] = []
|
|
|
683 |
for e in self {
|
|
|
684 |
if predicate(e) {
|
|
|
685 |
pass.append(e)
|
|
|
686 |
} else {
|
|
|
687 |
fail.append(e)
|
|
|
688 |
}
|
|
|
689 |
}
|
|
|
690 |
return (pass, fail)
|
|
|
691 |
}
|
|
|
692 |
}
|
|
|
693 |
|
|
|
694 |
internal final class OverflowingBuffer {
|
|
|
695 |
private var buffer = Data()
|
|
|
696 |
let capacity: Int
|
|
|
697 |
|
|
|
698 |
init(capacity: Int) {
|
|
|
699 |
self.capacity = capacity
|
|
|
700 |
}
|
|
|
701 |
|
|
|
702 |
func update(withData data: Data) -> Data {
|
|
|
703 |
if data.count >= capacity {
|
|
|
704 |
return sendAll(data: data)
|
|
|
705 |
} else if buffer.count + data.count <= capacity {
|
|
|
706 |
buffer.append(data)
|
|
|
707 |
return Data()
|
|
|
708 |
} else {
|
|
|
709 |
return sendSome(data: data)
|
|
|
710 |
}
|
|
|
711 |
}
|
|
|
712 |
|
|
|
713 |
func finalData() -> Data {
|
|
|
714 |
let result = buffer
|
|
|
715 |
buffer.count = 0
|
|
|
716 |
return result
|
|
|
717 |
}
|
|
|
718 |
|
|
|
719 |
private func sendAll(data: Data) -> Data {
|
|
|
720 |
let toSend = data.count - capacity
|
|
|
721 |
assert(toSend >= 0)
|
|
|
722 |
assert(data.count - toSend <= capacity)
|
|
|
723 |
|
|
|
724 |
var result = buffer
|
|
|
725 |
result.append(data.subdata(in: 0..<toSend))
|
|
|
726 |
|
|
|
727 |
buffer.count = 0
|
|
|
728 |
buffer.append(data.subdata(in: toSend..<data.count)) // TODO: Appending here to avoid later buffer growth, but maybe just buffer = data.subdata would be better
|
|
|
729 |
return result
|
|
|
730 |
}
|
|
|
731 |
|
|
|
732 |
private func sendSome(data: Data) -> Data {
|
|
|
733 |
let toSend = (buffer.count + data.count) - capacity
|
|
|
734 |
assert(toSend > 0) // If it were <= 0, we would have extended the array
|
|
|
735 |
assert(toSend < buffer.count) // If we would have sent everything, replaceBuffer should have been called
|
|
|
736 |
|
|
|
737 |
let result = buffer.subdata(in: 0..<toSend)
|
|
|
738 |
buffer.replaceSubrange(0..<toSend, with: Data())
|
|
|
739 |
buffer.append(data)
|
|
|
740 |
return result
|
|
|
741 |
}
|
|
|
742 |
}
|
|
|
743 |
|
|
|
744 |
/** Compare two Datas in time proportional to the untrusted data
|
|
|
745 |
|
|
|
746 |
Equatable-based comparisons generally stop comparing at the first difference.
|
|
|
747 |
This can be used by attackers, in some situations,
|
|
|
748 |
to determine a secret value by considering the time required to compare the values.
|
|
|
749 |
|
|
|
750 |
We enumerate over the untrusted values so that the time is proportaional to the attacker's data,
|
|
|
751 |
which provides the attack no information about the length of the secret.
|
|
|
752 |
*/
|
|
|
753 |
private func isEqualInConsistentTime(trusted: Data, untrusted: Data) -> Bool {
|
|
|
754 |
// The point of this routine is XOR the bytes of each data and accumulate the results with OR.
|
|
|
755 |
// If any bytes are different, then the OR will accumulate some non-0 value.
|
|
|
756 |
|
|
|
757 |
var result: UInt8 = untrusted.count == trusted.count ? 0 : 1 // Start with 0 (equal) only if our lengths are equal
|
|
|
758 |
for (i, untrustedByte) in untrusted.enumerated() {
|
|
|
759 |
// Use mod to wrap around ourselves if they are longer than we are.
|
|
|
760 |
// Remember, we already broke equality if our lengths are different.
|
|
|
761 |
result |= trusted[i % trusted.count] ^ untrustedByte
|
|
|
762 |
}
|
|
|
763 |
|
|
|
764 |
return result == 0
|
|
|
765 |
}
|