Proyectos de Subversion Iphone Microlearning

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
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
}