123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780 |
- //
- // RNCryptor.swift
- //
- // Copyright © 2015 Rob Napier. All rights reserved.
- //
- // This code is licensed under the MIT License:
- //
- // Permission is hereby granted, free of charge, to any person obtaining a
- // copy of this software and associated documentation files (the "Software"),
- // to deal in the Software without restriction, including without limitation
- // the rights to use, copy, modify, merge, publish, distribute, sublicense,
- // and/or sell copies of the Software, and to permit persons to whom the
- // Software is furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- // DEALINGS IN THE SOFTWARE.
- //
- import Foundation
- /// The `RNCryptorType` protocol defines generic API to a mutable,
- /// incremental, password-based encryptor or decryptor. Its generic
- /// usage is as follows:
- ///
- /// let cryptor = Encryptor(password: "mypassword")
- /// // or Decryptor()
- ///
- /// var result NSMutableData
- /// for data in datas {
- /// result.appendData(try cryptor.update(data))
- /// }
- /// result.appendData(try cryptor.final())
- ///
- /// After calling `finalData()`, the cryptor is no longer valid.
- public protocol RNCryptorType {
- /// Creates and returns a cryptor.
- ///
- /// - parameter password: Non-empty password string. This will be interpretted as UTF-8.
- init(password: String)
- /// Updates cryptor with data and returns processed data.
- ///
- /// - parameter data: Data to process. May be empty.
- /// - throws: `Error`
- /// - returns: Processed data. May be empty.
- func updateWithData(data: NSData) throws -> NSData
- /// Returns trailing data and invalidates the cryptor.
- ///
- /// - throws: `Error`
- /// - returns: Trailing data
- func finalData() throws -> NSData
- }
- public extension RNCryptorType {
- /// Simplified, generic interface to `RNCryptorType`. Takes a data,
- /// returns a processed data. Generally you should use
- /// `RNCryptor.encryptData(password:)`, or
- /// `RNCryptor.decryptData(password:)` instead, but this is useful
- /// for code that is neutral on whether it is encrypting or decrypting.
- ///
- /// - throws: `Error`
- private func oneshot(data: NSData) throws -> NSData {
- let result = NSMutableData(data: try updateWithData(data))
- result.appendData(try finalData())
- return result
- }
- }
- // FIXME: Move this to RNCryptor.Error if @objc can rename it correctly.
- /// Errors thrown by `RNCryptorType`.
- @objc public enum RNCryptorError: Int, ErrorType {
- /// Ciphertext was corrupt or password was incorrect.
- /// It is not possible to distinguish between these cases in the v3 data format.
- case HMACMismatch = 1
- /// Unrecognized data format. Usually this means the data is corrupt.
- case UnknownHeader = 2
- /// `final()` was called before sufficient data was passed to `updateWithData()`
- case MessageTooShort
- /// Memory allocation failure. This should never happen.
- case MemoryFailure
- /// A password-based decryptor was used on a key-based ciphertext, or vice-versa.
- case InvalidCredentialType
- }
- /// RNCryptor encryption/decryption interface.
- public class RNCryptor: NSObject {
- /// Encrypt data using password and return encrypted data.
- public static func encryptData(data: NSData, password: String) -> NSData {
- return Encryptor(password: password).encryptData(data)
- }
- /// Decrypt data using password and return decrypted data. Throws if
- /// password is incorrect or ciphertext is in the wrong format.
- /// - throws `Error`
- public static func decryptData(data: NSData, password: String) throws -> NSData {
- return try Decryptor(password: password).decryptData(data)
- }
- /// Generates random NSData of given length
- /// Crashes if `length` is larger than allocatable memory, or if the system random number generator is not available.
- public static func randomDataOfLength(length: Int) -> NSData {
- let data = NSMutableData(length: length)!
- let result = SecRandomCopyBytes(kSecRandomDefault, length, UnsafeMutablePointer<UInt8>(data.mutableBytes))
- guard result == errSecSuccess else {
- fatalError("SECURITY FAILURE: Could not generate secure random numbers: \(result).")
- }
- return data
- }
- /// A encryptor for the latest data format. If compatibility with other RNCryptor
- /// implementations is required, you may wish to use the specific encryptor version rather
- /// than accepting "latest."
- ///
- @objc(RNEncryptor)
- public final class Encryptor: NSObject, RNCryptorType {
- private let encryptor: EncryptorV3
- /// Creates and returns a cryptor.
- ///
- /// - parameter password: Non-empty password string. This will be interpretted as UTF-8.
- public init(password: String) {
- precondition(password != "")
- encryptor = EncryptorV3(password: password)
- }
- /// Updates cryptor with data and returns processed data.
- ///
- /// - parameter data: Data to process. May be empty.
- /// - returns: Processed data. May be empty.
- public func updateWithData(data: NSData) -> NSData {
- return encryptor.updateWithData(data)
- }
- /// Returns trailing data and invalidates the cryptor.
- ///
- /// - returns: Trailing data
- public func finalData() -> NSData {
- return encryptor.finalData()
- }
- /// Simplified, generic interface to `RNCryptorType`. Takes a data,
- /// returns a processed data, and invalidates the cryptor.
- public func encryptData(data: NSData) -> NSData {
- return encryptor.encryptData(data)
- }
- }
- /// Password-based decryptor that can handle any supported format.
- @objc(RNDecryptor)
- public final class Decryptor : NSObject, RNCryptorType {
- private var decryptors: [VersionedDecryptorType.Type] = [DecryptorV3.self]
- private var buffer = NSMutableData()
- private var decryptor: RNCryptorType?
- private let password: String
- /// Creates and returns a cryptor.
- ///
- /// - parameter password: Non-empty password string. This will be interpretted as UTF-8.
- public init(password: String) {
- assert(password != "")
- self.password = password
- }
- /// Decrypt data using password and return decrypted data, invalidating decryptor. Throws if
- /// password is incorrect or ciphertext is in the wrong format.
- /// - throws `Error`
- public func decryptData(data: NSData) throws -> NSData {
- return try oneshot(data)
- }
- /// Updates cryptor with data and returns processed data.
- ///
- /// - parameter data: Data to process. May be empty.
- /// - throws: `Error`
- /// - returns: Processed data. May be empty.
- public func updateWithData(data: NSData) throws -> NSData {
- if let d = decryptor {
- return try d.updateWithData(data)
- }
- buffer.appendData(data)
- let toCheck:[VersionedDecryptorType.Type]
- (toCheck, decryptors) = decryptors.splitPassFail{ self.buffer.length >= $0.preambleSize }
- for decryptorType in toCheck {
- if decryptorType.canDecrypt(buffer.bytesView[0..<decryptorType.preambleSize]) {
- let d = decryptorType.init(password: password)
- decryptor = d
- let result = try d.updateWithData(buffer)
- buffer.length = 0
- return result
- }
- }
- guard !decryptors.isEmpty else { throw RNCryptorError.UnknownHeader }
- return NSData()
- }
- /// Returns trailing data and invalidates the cryptor.
- ///
- /// - throws: `Error`
- /// - returns: Trailing data
- public func finalData() throws -> NSData {
- guard let d = decryptor else {
- throw RNCryptorError.UnknownHeader
- }
- return try d.finalData()
- }
- }
- }
- // V3 implementaion
- public extension RNCryptor {
- /// V3 format settings
- @objc(RNCryptorFormatV3)
- public final class FormatV3: NSObject {
- /// Size of AES and HMAC keys
- public static let keySize = kCCKeySizeAES256
- /// Size of PBKDF2 salt
- public static let saltSize = 8
- /// Generate a key from a password and salt
- /// - parameters:
- /// - password: Password to convert
- /// - salt: Salt. Generally constructed with RNCryptor.randomDataOfLength(FormatV3.saltSize)
- /// - returns: Key of length FormatV3.keySize
- public static func keyForPassword(password: String, salt: NSData) -> NSData {
- let derivedKey = NSMutableData(length: keySize)!
- let derivedKeyPtr = UnsafeMutablePointer<UInt8>(derivedKey.mutableBytes)
- let passwordData = password.dataUsingEncoding(NSUTF8StringEncoding)!
- let passwordPtr = UnsafePointer<Int8>(passwordData.bytes)
- let saltPtr = UnsafePointer<UInt8>(salt.bytes)
- // All the crazy casting because CommonCryptor hates Swift
- let algorithm = CCPBKDFAlgorithm(kCCPBKDF2)
- let prf = CCPseudoRandomAlgorithm(kCCPRFHmacAlgSHA1)
- let pbkdf2Rounds = UInt32(10000)
- let result = CCKeyDerivationPBKDF(
- algorithm,
- passwordPtr, passwordData.length,
- saltPtr, salt.length,
- prf, pbkdf2Rounds,
- derivedKeyPtr, derivedKey.length)
- guard result == CCCryptorStatus(kCCSuccess) else {
- fatalError("SECURITY FAILURE: Could not derive secure password (\(result)): \(derivedKey).")
- }
- return derivedKey
- }
- static let formatVersion = UInt8(3)
- static let ivSize = kCCBlockSizeAES128
- static let hmacSize = Int(CC_SHA256_DIGEST_LENGTH)
- static let keyHeaderSize = 1 + 1 + kCCBlockSizeAES128
- static let passwordHeaderSize = 1 + 1 + 8 + 8 + kCCBlockSizeAES128
- }
- /// Format version 3 encryptor. Use this to ensure a specific format verison
- /// or when using keys (which are inherrently versions-specific). To use
- /// "the latest encryptor" with a password, use `Encryptor` instead.
- @objc(RNEncryptorV3)
- public final class EncryptorV3 : NSObject, RNCryptorType {
- private var engine: Engine
- private var hmac: HMACV3
- private var pendingHeader: NSData?
- /// Creates and returns an encryptor.
- ///
- /// - parameter password: Non-empty password string. This will be interpretted as UTF-8.
- public convenience init(password: String) {
- self.init(
- password: password,
- encryptionSalt: RNCryptor.randomDataOfLength(V3.saltSize),
- hmacSalt: RNCryptor.randomDataOfLength(V3.saltSize),
- iv: RNCryptor.randomDataOfLength(V3.ivSize))
- }
- /// Creates and returns an encryptor using keys.
- ///
- /// - Attention: This method requires some expertise to use correctly.
- /// Most users should use `init(password:)` which is simpler
- /// to use securely.
- ///
- /// Keys should not be generated directly from strings (`.dataUsingEncoding()` or similar).
- /// Ideally, keys should be random (`Cryptor.randomDataOfLength()` or some other high-quality
- /// random generator. If keys must be generated from strings, then use `FormatV3.keyForPassword(salt:)`
- /// with a random salt, or just use password-based encryption (that's what it's for).
- ///
- /// - parameters:
- /// - encryptionKey: AES-256 key. Must be exactly FormatV3.keySize (kCCKeySizeAES256, 32 bytes)
- /// - hmacKey: HMAC key. Must be exactly FormatV3.keySize (kCCKeySizeAES256, 32 bytes)
- public convenience init(encryptionKey: NSData, hmacKey: NSData) {
- self.init(encryptionKey: encryptionKey, hmacKey: hmacKey, iv: RNCryptor.randomDataOfLength(V3.ivSize))
- }
- /// Takes a data, returns a processed data, and invalidates the cryptor.
- public func encryptData(data: NSData) -> NSData {
- return try! oneshot(data)
- }
- /// Updates cryptor with data and returns encrypted data.
- ///
- /// - parameter data: Data to process. May be empty.
- /// - returns: Processed data. May be empty.
- public func updateWithData(data: NSData) -> NSData {
- // It should not be possible for this to fail during encryption
- return try! handle(engine.updateWithData(data))
- }
- /// Returns trailing data and invalidates the cryptor.
- ///
- /// - returns: Trailing data
- public func finalData() -> NSData {
- let result = NSMutableData(data: try! handle(engine.finalData()))
- result.appendData(hmac.finalData())
- return result
- }
- // Expose random numbers for testing
- internal convenience init(encryptionKey: NSData, hmacKey: NSData, iv: NSData) {
- let preamble = [V3.formatVersion, UInt8(0)]
- let header = NSMutableData(bytes: preamble, length: preamble.count)
- header.appendData(iv)
- self.init(encryptionKey: encryptionKey, hmacKey: hmacKey, iv: iv, header: header)
- }
- // Expose random numbers for testing
- internal convenience init(password: String, encryptionSalt: NSData, hmacSalt: NSData, iv: NSData) {
- let encryptionKey = V3.keyForPassword(password, salt: encryptionSalt)
- let hmacKey = V3.keyForPassword(password, salt: hmacSalt)
- // TODO: This chained-+ is very slow to compile in Swift 2b5 (http://www.openradar.me/21842206)
- // let header = [V3.version, UInt8(1)] + encryptionSalt + hmacSalt + iv
- let preamble = [V3.formatVersion, UInt8(1)]
- let header = NSMutableData(bytes: preamble, length: preamble.count)
- header.appendData(encryptionSalt)
- header.appendData(hmacSalt)
- header.appendData(iv)
- self.init(encryptionKey: encryptionKey, hmacKey: hmacKey, iv: iv, header: header)
- }
- private init(encryptionKey: NSData, hmacKey: NSData, iv: NSData, header: NSData) {
- precondition(encryptionKey.length == V3.keySize)
- precondition(hmacKey.length == V3.keySize)
- precondition(iv.length == V3.ivSize)
- hmac = HMACV3(key: hmacKey)
- engine = Engine(operation: .Encrypt, key: encryptionKey, iv: iv)
- pendingHeader = header
- }
- private func handle(data: NSData) -> NSData {
- var result: NSData
- if let ph = pendingHeader {
- let accum = NSMutableData(data: ph)
- pendingHeader = nil
- accum.appendData(data)
- result = accum
- } else {
- result = data
- }
- hmac.updateWithData(result)
- return result
- }
- }
- /// Format version 3 decryptor. This is required in order to decrypt
- /// using keys (since key configuration is version-specific). For password
- /// decryption, `Decryptor` is generally preferred, and will call this
- /// if appropriate.
- @objc(RNDecryptorV3)
- public final class DecryptorV3: NSObject, VersionedDecryptorType {
- //
- // Static methods
- //
- private static let preambleSize = 1
- private static func canDecrypt(preamble: NSData) -> Bool {
- assert(preamble.length >= 1)
- return preamble.bytesView[0] == 3
- }
- //
- // Private properties
- //
- private var buffer = NSMutableData()
- private var decryptorEngine: DecryptorEngineV3?
- private let credential: Credential
- /// Creates and returns a decryptor.
- ///
- /// - parameter password: Non-empty password string. This will be interpretted as UTF-8.
- public init(password: String) {
- credential = .Password(password)
- }
- /// Creates and returns a decryptor using keys.
- ///
- /// - parameters:
- /// - encryptionKey: AES-256 key. Must be exactly FormatV3.keySize (kCCKeySizeAES256, 32 bytes)
- /// - hmacKey: HMAC key. Must be exactly FormatV3.keySize (kCCKeySizeAES256, 32 bytes)
- public init(encryptionKey: NSData, hmacKey: NSData) {
- precondition(encryptionKey.length == V3.keySize)
- precondition(hmacKey.length == V3.hmacSize)
- credential = .Keys(encryptionKey: encryptionKey, hmacKey: hmacKey)
- }
- /// Decrypt data using password and return decrypted data. Throws if
- /// password is incorrect or ciphertext is in the wrong format.
- /// - throws `Error`
- public func decryptData(data: NSData) throws -> NSData {
- return try oneshot(data)
- }
- /// Updates cryptor with data and returns encrypted data.
- ///
- /// - parameter data: Data to process. May be empty.
- /// - returns: Processed data. May be empty.
- public func updateWithData(data: NSData) throws -> NSData {
- if let e = decryptorEngine {
- return try e.updateWithData(data)
- }
- buffer.appendData(data)
- guard buffer.length >= requiredHeaderSize else {
- return NSData()
- }
- let e = try createEngineWithCredential(credential, header: buffer.bytesView[0..<requiredHeaderSize])
- decryptorEngine = e
- let body = buffer.bytesView[requiredHeaderSize..<buffer.length]
- buffer.length = 0
- return try e.updateWithData(body)
- }
- /// Returns trailing data and invalidates the cryptor.
- ///
- /// - returns: Trailing data
- public func finalData() throws -> NSData {
- guard let result = try decryptorEngine?.finalData() else {
- throw RNCryptorError.MessageTooShort
- }
- return result
- }
- //
- // Private functions
- //
- private var requiredHeaderSize: Int {
- switch credential {
- case .Password(_): return V3.passwordHeaderSize
- case .Keys(_, _): return V3.keyHeaderSize
- }
- }
- private func createEngineWithCredential(credential: Credential, header: NSData) throws -> DecryptorEngineV3 {
- switch credential {
- case let .Password(password):
- return try createEngineWithPassword(password, header: header)
- case let .Keys(encryptionKey, hmacKey):
- return try createEngineWithKeys(encryptionKey: encryptionKey, hmacKey: hmacKey, header: header)
- }
- }
- private func createEngineWithPassword(password: String, header: NSData) throws -> DecryptorEngineV3 {
- assert(password != "")
- precondition(header.length == V3.passwordHeaderSize)
- guard DecryptorV3.canDecrypt(header) else {
- throw RNCryptorError.UnknownHeader
- }
- guard header.bytesView[1] == 1 else {
- throw RNCryptorError.InvalidCredentialType
- }
- let encryptionSalt = header.bytesView[2...9]
- let hmacSalt = header.bytesView[10...17]
- let iv = header.bytesView[18...33]
- let encryptionKey = V3.keyForPassword(password, salt: encryptionSalt)
- let hmacKey = V3.keyForPassword(password, salt: hmacSalt)
- return DecryptorEngineV3(encryptionKey: encryptionKey, hmacKey: hmacKey, iv: iv, header: header)
- }
- private func createEngineWithKeys(encryptionKey encryptionKey: NSData, hmacKey: NSData, header: NSData) throws -> DecryptorEngineV3 {
- precondition(header.length == V3.keyHeaderSize)
- precondition(encryptionKey.length == V3.keySize)
- precondition(hmacKey.length == V3.keySize)
- guard DecryptorV3.canDecrypt(header) else {
- throw RNCryptorError.UnknownHeader
- }
- guard header.bytesView[1] == 0 else {
- throw RNCryptorError.InvalidCredentialType
- }
- let iv = header.bytesView[2..<18]
- return DecryptorEngineV3(encryptionKey: encryptionKey, hmacKey: hmacKey, iv: iv, header: header)
- }
- }
- }
- private let CCErrorDomain = "com.apple.CommonCrypto"
- internal enum CryptorOperation: CCOperation {
- case Encrypt = 0 // CCOperation(kCCEncrypt)
- case Decrypt = 1 // CCOperation(kCCDecrypt)
- }
- internal final class Engine {
- private let cryptor: CCCryptorRef
- private var buffer = NSMutableData()
- init(operation: CryptorOperation, key: NSData, iv: NSData) {
- var cryptorOut = CCCryptorRef()
- let result = CCCryptorCreate(
- operation.rawValue,
- CCAlgorithm(kCCAlgorithmAES128), CCOptions(kCCOptionPKCS7Padding),
- key.bytes, key.length,
- iv.bytes,
- &cryptorOut
- )
- cryptor = cryptorOut
- // It is a programming error to create us with illegal values
- // This is an internal class, so we can constrain what is sent to us.
- // If this is ever made public, it should throw instead of asserting.
- assert(result == CCCryptorStatus(kCCSuccess))
- }
- deinit {
- if cryptor != CCCryptorRef() {
- CCCryptorRelease(cryptor)
- }
- }
- func sizeBufferForDataOfLength(length: Int) -> Int {
- let size = CCCryptorGetOutputLength(cryptor, length, true)
- buffer.length = size
- return size
- }
- func updateWithData(data: NSData) throws -> NSData {
- let outputLength = sizeBufferForDataOfLength(data.length)
- var dataOutMoved: Int = 0
- var result: CCCryptorStatus = CCCryptorStatus(kCCUnimplemented)
- result = CCCryptorUpdate(
- cryptor,
- data.bytes, data.length,
- buffer.mutableBytes, outputLength,
- &dataOutMoved)
- // The only error returned by CCCryptorUpdate is kCCBufferTooSmall, which would be a programming error
- assert(result == CCCryptorStatus(kCCSuccess))
- buffer.length = dataOutMoved
- return buffer
- }
- func finalData() throws -> NSData {
- let outputLength = sizeBufferForDataOfLength(0)
- var dataOutMoved: Int = 0
- let result = CCCryptorFinal(
- cryptor,
- buffer.mutableBytes, outputLength,
- &dataOutMoved
- )
- guard result == CCCryptorStatus(kCCSuccess) else {
- throw NSError(domain: CCErrorDomain, code: Int(result), userInfo: nil)
- }
- buffer.length = dataOutMoved
- return buffer
- }
- }
- internal typealias V3 = RNCryptor.FormatV3
- private enum Credential {
- case Password(String)
- case Keys(encryptionKey: NSData, hmacKey: NSData)
- }
- private final class DecryptorEngineV3 {
- private let buffer = OverflowingBuffer(capacity: V3.hmacSize)
- private var hmac: HMACV3
- private var engine: Engine
- init(encryptionKey: NSData, hmacKey: NSData, iv: NSData, header: NSData) {
- precondition(encryptionKey.length == V3.keySize)
- precondition(hmacKey.length == V3.hmacSize)
- precondition(iv.length == V3.ivSize)
- hmac = HMACV3(key: hmacKey)
- hmac.updateWithData(header)
- engine = Engine(operation: .Decrypt, key: encryptionKey, iv: iv)
- }
- func updateWithData(data: NSData) throws -> NSData {
- let overflow = buffer.updateWithData(data)
- hmac.updateWithData(overflow)
- return try engine.updateWithData(overflow)
- }
- func finalData() throws -> NSData {
- let result = try engine.finalData()
- let hash = hmac.finalData()
- if !isEqualInConsistentTime(trusted: hash, untrusted: buffer.finalData()) {
- throw RNCryptorError.HMACMismatch
- }
- return result
- }
- }
- private final class HMACV3 {
- var context: CCHmacContext = CCHmacContext()
- init(key: NSData) {
- CCHmacInit(
- &context,
- CCHmacAlgorithm(kCCHmacAlgSHA256),
- key.bytes,
- key.length
- )
- }
- func updateWithData(data: NSData) {
- CCHmacUpdate(&context, data.bytes, data.length)
- }
- func finalData() -> NSData {
- let hmac = NSMutableData(length: V3.hmacSize)!
- CCHmacFinal(&context, hmac.mutableBytes)
- return hmac
- }
- }
- // Internal protocol for version-specific decryptors.
- private protocol VersionedDecryptorType: RNCryptorType {
- static var preambleSize: Int { get }
- static func canDecrypt(preamble: NSData) -> Bool
- init(password: String)
- }
- private extension CollectionType {
- // Split collection into ([pass], [fail]) based on predicate.
- func splitPassFail(pred: Generator.Element -> Bool) -> ([Generator.Element], [Generator.Element]) {
- var pass: [Generator.Element] = []
- var fail: [Generator.Element] = []
- for e in self {
- if pred(e) {
- pass.append(e)
- } else {
- fail.append(e)
- }
- }
- return (pass, fail)
- }
- }
- internal class OverflowingBuffer {
- private var buffer = NSMutableData()
- let capacity: Int
- init(capacity: Int) {
- self.capacity = capacity
- }
- @warn_unused_result
- func updateWithData(data: NSData) -> NSData {
- if data.length >= capacity {
- return sendAllArray(data)
- } else if buffer.length + data.length <= capacity {
- buffer.appendData(data)
- return NSData()
- } else {
- return sendSomeArray(data)
- }
- }
- func finalData() -> NSData {
- let result = buffer
- buffer = NSMutableData() // Data belongs to caller now.
- return result
- }
- private func sendAllArray(data: NSData) -> NSData {
- let toSend = data.length - capacity
- assert(toSend >= 0)
- assert(data.length - toSend <= capacity)
- let result = NSMutableData(data: buffer)
- result.appendData(data.bytesView[0..<toSend])
- buffer.length = 0
- buffer.appendData(data.bytesView[toSend..<data.length])
- return result
- }
- private func sendSomeArray(data: NSData) -> NSData {
- let toSend = (buffer.length + data.length) - capacity
- assert(toSend > 0) // If it were <= 0, we would have extended the array
- assert(toSend < buffer.length) // If we would have sent everything, replaceBuffer should have been called
- let result = buffer.bytesView[0..<toSend]
- buffer.replaceBytesInRange(NSRange(0..<toSend), withBytes: nil, length: 0)
- buffer.appendData(data)
- return result
- }
- }
- private extension NSData {
- var bytesView: BytesView { return BytesView(self) }
- }
- private struct BytesView: CollectionType {
- let data: NSData
- init(_ data: NSData) { self.data = data }
-
- subscript (position: Int) -> UInt8 {
- return UnsafePointer<UInt8>(data.bytes)[position]
- }
- subscript (bounds: Range<Int>) -> NSData {
- return data.subdataWithRange(NSRange(bounds))
- }
- var startIndex: Int = 0
- var endIndex: Int { return data.length }
- }
- /** Compare two NSData in time proportional to the untrusted data
- Equatable-based comparisons genreally stop comparing at the first difference.
- This can be used by attackers, in some situations,
- to determine a secret value by considering the time required to compare the values.
- We enumerate over the untrusted values so that the time is proportaional to the attacker's data,
- which provides the attack no informatoin about the length of the secret.
- */
- private func isEqualInConsistentTime(trusted trusted: NSData, untrusted: NSData) -> Bool {
- // The point of this routine is XOR the bytes of each data and accumulate the results with OR.
- // If any bytes are different, then the OR will accumulate some non-0 value.
-
- var result: UInt8 = untrusted.length == trusted.length ? 0 : 1 // Start with 0 (equal) only if our lengths are equal
- for (i, untrustedByte) in untrusted.bytesView.enumerate() {
- // Use mod to wrap around ourselves if they are longer than we are.
- // Remember, we already broke equality if our lengths are different.
- result |= trusted.bytesView[i % trusted.length] ^ untrustedByte
- }
-
- return result == 0
- }
|