Skip to content

Commit

Permalink
Remove impossible throws
Browse files Browse the repository at this point in the history
  • Loading branch information
rnapier committed Oct 4, 2015
1 parent 6717202 commit 0092626
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 41 deletions.
8 changes: 7 additions & 1 deletion README.md
Expand Up @@ -69,7 +69,7 @@ if (error != nil) {
## Incremental Usage
RNCryptor suports incremental use, specifically designed to work with `NSURLSession`. This is also useful for cases where the encrypted or decrypted data will not comfortably fit in memory.
RNCryptor supports incremental use, for example when using with `NSURLSession`. This is also useful for cases where the encrypted or decrypted data will not comfortably fit in memory.
To operate in incremental mode, you create an `Encryptor` or `Decryptor`, call `updateWithData()` repeatedly, gathering its results, and then call `finalData()` and gather its result.
Expand Down Expand Up @@ -232,6 +232,12 @@ RNDecryptor *decryptor = [[[RNDecryptorV3 alloc] initWithEncryptionKey:encryptio
## FAQ
### How do I detect an incorrect password?
If you decrypt with the wrong password, you will receive an `HMACMismatch` error. This is the same error you will receive if your ciphertext is corrupted. You won't receive this error until the entire message has been decrypted (during the call to `finalData()`).
The v3 data format has no way to detect incorrect passwords directly. It just decrypts gibberish, and then uses the HMAC (a kind of encrypted hash) to determine that the result is corrupt.
### Can I use RNCryptor to read and write my non-RNCryptor data format?
No. RNCryptor implements a specific data format. It is not a general-purpose encryption library. If you have created your own data format, you will need to write specific code to deal with whatever you created. Please make sure the data format you've invented is secure. (This is much harder than it sounds.)
Expand Down
34 changes: 16 additions & 18 deletions RNCryptor.swift
Expand Up @@ -326,14 +326,14 @@ public extension RNCryptor {
/// - 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))
return 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()))
let result = NSMutableData(data: handle(engine.finalData()))
result.appendData(hmac.finalData())
return result
}
Expand Down Expand Up @@ -440,7 +440,7 @@ public extension RNCryptor {
/// - returns: Processed data. May be empty.
public func updateWithData(data: NSData) throws -> NSData {
if let e = decryptorEngine {
return try e.updateWithData(data)
return e.updateWithData(data)
}

buffer.appendData(data)
Expand All @@ -452,7 +452,7 @@ public extension RNCryptor {
decryptorEngine = e
let body = buffer.bytesView[requiredHeaderSize..<buffer.length]
buffer.length = 0
return try e.updateWithData(body)
return e.updateWithData(body)
}

/// Returns trailing data and invalidates the cryptor.
Expand Down Expand Up @@ -526,8 +526,6 @@ public extension RNCryptor {
}
}

private let CCErrorDomain = "com.apple.CommonCrypto"

internal enum CryptorOperation: CCOperation {
case Encrypt = 0 // CCOperation(kCCEncrypt)
case Decrypt = 1 // CCOperation(kCCDecrypt)
Expand Down Expand Up @@ -566,26 +564,24 @@ internal final class Engine {
return size
}

func updateWithData(data: NSData) throws -> NSData {
func updateWithData(data: NSData) -> NSData {
let outputLength = sizeBufferForDataOfLength(data.length)
var dataOutMoved: Int = 0

var result: CCCryptorStatus = CCCryptorStatus(kCCUnimplemented)

result = CCCryptorUpdate(
let 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))
assert(result == CCCryptorStatus(kCCSuccess), "RNCRYPTOR BUG. PLEASE REPORT.")

buffer.length = dataOutMoved
return buffer
}

func finalData() throws -> NSData {
func finalData() -> NSData {
let outputLength = sizeBufferForDataOfLength(0)
var dataOutMoved: Int = 0

Expand All @@ -595,9 +591,11 @@ internal final class Engine {
&dataOutMoved
)

guard result == CCCryptorStatus(kCCSuccess) else {
throw NSError(domain: CCErrorDomain, code: Int(result), userInfo: nil)
}
// Note that since iOS 6, CCryptor will never return padding errors or other decode errors.
// I'm not aware of any non-catestrophic (MemoryAllocation) situation in which this
// can fail. Using assert() just in case, but we'll ignore errors in Release.
// https://devforums.apple.com/message/920802#920802
assert(result == CCCryptorStatus(kCCSuccess), "RNCRYPTOR BUG. PLEASE REPORT.")

buffer.length = dataOutMoved
return buffer
Expand Down Expand Up @@ -626,14 +624,14 @@ private final class DecryptorEngineV3 {
engine = Engine(operation: .Decrypt, key: encryptionKey, iv: iv)
}

func updateWithData(data: NSData) throws -> NSData {
func updateWithData(data: NSData) -> NSData {
let overflow = buffer.updateWithData(data)
hmac.updateWithData(overflow)
return try engine.updateWithData(overflow)
return engine.updateWithData(overflow)
}

func finalData() throws -> NSData {
let result = try engine.finalData()
let result = engine.finalData()
let hash = hmac.finalData()
if !isEqualInConsistentTime(trusted: hash, untrusted: buffer.finalData()) {
throw RNCryptorError.HMACMismatch
Expand Down
60 changes: 38 additions & 22 deletions Tests/RNCryptorTests.swift
Expand Up @@ -26,6 +26,14 @@ import XCTest

@testable import RNCryptor

func randomLength() -> Int {
return Int(arc4random_uniform(1024) + 1)
}

func randomData() -> NSData {
return RNCryptor.randomDataOfLength(randomLength())
}

class RNCryptorTests: XCTestCase {
func testRandomData() {
let len = 1024
Expand All @@ -45,27 +53,19 @@ class RNCryptorTests: XCTestCase {
}

func testEngine() {
let data = RNCryptor.randomDataOfLength(1024)
let data = randomData()
let encryptKey = RNCryptor.randomDataOfLength(V3.keySize)
let iv = RNCryptor.randomDataOfLength(V3.ivSize)

let encrypted = NSMutableData()
do {
let encryptor = Engine(operation: .Encrypt, key: encryptKey, iv: iv)
encrypted.appendData(try encryptor.updateWithData(data))
encrypted.appendData(try encryptor.finalData())
} catch {
XCTFail("Caught: \(error)")
}

do {
let decryptor = Engine(operation: .Decrypt, key: encryptKey, iv: iv)
let decrypted = NSMutableData(data:try decryptor.updateWithData(encrypted))
decrypted.appendData(try decryptor.finalData())
XCTAssertEqual(decrypted, data)
} catch {
XCTFail("Caught: \(error)")
}
let encryptor = Engine(operation: .Encrypt, key: encryptKey, iv: iv)
encrypted.appendData(encryptor.updateWithData(data))
encrypted.appendData(encryptor.finalData())

let decryptor = Engine(operation: .Decrypt, key: encryptKey, iv: iv)
let decrypted = NSMutableData(data:decryptor.updateWithData(encrypted))
decrypted.appendData(decryptor.finalData())
XCTAssertEqual(decrypted, data)
}

func testKeyEncryptor() {
Expand Down Expand Up @@ -127,7 +127,7 @@ class RNCryptorTests: XCTestCase {
func testOneShotKey() {
let encryptionKey = RNCryptor.randomDataOfLength(V3.keySize)
let hmacKey = RNCryptor.randomDataOfLength(V3.keySize)
let data = RNCryptor.randomDataOfLength(1024)
let data = randomData()

let ciphertext = RNCryptor.EncryptorV3(encryptionKey: encryptionKey, hmacKey: hmacKey).encryptData(data)

Expand All @@ -144,7 +144,7 @@ class RNCryptorTests: XCTestCase {

func testOneShotPassword() {
let password = "thepassword"
let data = RNCryptor.randomDataOfLength(1024)
let data = randomData()

let ciphertext = RNCryptor.Encryptor(password: password).encryptData(data)

Expand All @@ -161,7 +161,7 @@ class RNCryptorTests: XCTestCase {

func testMultipleUpdateWithData() {
let password = "thepassword"
let datas = (0..<10).map{ _ in RNCryptor.randomDataOfLength(1024) }
let datas = (0..<10).map{ _ in randomData() }
let fullData = datas.reduce(NSMutableData()) { $0.appendData($1); return $0 }

let encryptor = RNCryptor.Encryptor(password: password)
Expand All @@ -180,7 +180,7 @@ class RNCryptorTests: XCTestCase {
}

func testBadFormat() {
let data = NSMutableData(length: 1024)!
let data = NSMutableData(length: randomLength())!
do {
try RNCryptor.Decryptor(password: "password").decryptData(data)
XCTFail("Should not thrown")
Expand All @@ -192,7 +192,7 @@ class RNCryptorTests: XCTestCase {
}

func testBadFormatV3() {
let data = NSMutableData(length: 1024)!
let data = NSMutableData(length: randomLength())!
do {
try RNCryptor.DecryptorV3(password: "password").decryptData(data)
XCTFail("Should not thrown")
Expand All @@ -202,4 +202,20 @@ class RNCryptorTests: XCTestCase {
XCTFail("Threw wrong thing \(error)")
}
}

func testBadPassword() {
let password = "thepassword"
let data = randomData()

let ciphertext = RNCryptor.Encryptor(password: password).encryptData(data)

do {
let _ = try RNCryptor.Decryptor(password: "wrongpassword").decryptData(ciphertext)
XCTFail("Should have failed to decrypt")
} catch let err as RNCryptorError {
XCTAssertEqual(err, RNCryptorError.HMACMismatch)
} catch {
XCTFail("Wrong error: \(error)")
}
}
}

0 comments on commit 0092626

Please sign in to comment.