ably-cocoa/Spec/Crypto.swift

183 lines
8.3 KiB
Swift

import Ably
import Nimble
import Quick
import SwiftyJSON
class Crypto : QuickSpec {
override func spec() {
describe("Crypto") {
let key = "+/h4eHh4eHh4eHh4eHh4eA=="
let binaryKey = Data(base64Encoded: key, options: .ignoreUnknownCharacters)!
let longKey = NSMutableData(data: binaryKey)
longKey.append(binaryKey)
// RSE1
context("getDefaultParams") {
// RSE1a, RSE1b
it("returns a complete CipherParams instance, using the default values for any field not supplied") {
expect{ARTCrypto.getDefaultParams(["nokey":"nokey"])}.to(raiseException())
var params: ARTCipherParams = ARTCrypto.getDefaultParams([
"key": key
])
expect(params.algorithm).to(equal("AES"))
expect(params.key).to(equal(binaryKey as Data))
expect(params.keyLength).to(equal(128))
expect(params.mode).to(equal("CBC"))
params = ARTCrypto.getDefaultParams([
"key": longKey,
"algorithm": "DES"
])
expect(params.algorithm).to(equal("DES"))
expect(params.key).to(equal(longKey as Data))
expect(params.keyLength).to(equal(256))
expect(params.mode).to(equal("CBC"))
}
// RSE1c
context("key parameter") {
it("can be a binary") {
let params = ARTCrypto.getDefaultParams(["key": binaryKey])
expect(params.key).to(equal(binaryKey as Data))
}
it("can be a base64-encoded string with standard encoding") {
let params = ARTCrypto.getDefaultParams(["key": key])
expect(params.key).to(equal(binaryKey as Data))
}
it("can be a base64-encoded string with URL encoding") {
let key = "-_h4eHh4eHh4eHh4eHh4eA=="
let params = ARTCrypto.getDefaultParams(["key": key])
expect(params.key).to(equal(binaryKey as Data))
}
}
// RSE1d
it("calculates a keyLength from the key (its size in bits)") {
var params = ARTCrypto.getDefaultParams(["key": binaryKey])
expect(params.keyLength).to(equal(128))
params = ARTCrypto.getDefaultParams(["key": longKey])
expect(params.keyLength).to(equal(256))
}
// RSE1e
it("should check that keyLength is valid for algorithm") {
expect{ARTCrypto.getDefaultParams([
"key": binaryKey.subdata(in: 0..<10)
])}.to(raiseException())
}
}
// RSE2
context("generateRandomKey") {
// RSE2a, RSE2b
it("takes a single length argument and returns a binary") {
var key: NSData = ARTCrypto.generateRandomKey(128) as NSData
expect(key.length).to(equal(128 / 8))
key = ARTCrypto.generateRandomKey(256) as NSData
expect(key.length).to(equal(256 / 8))
}
// RSE2a, RSE2b
it("takes no arguments and returns the default algorithm's default length") {
let key: NSData = ARTCrypto.generateRandomKey() as NSData
expect(key.length).to(equal(256 / 8))
}
}
context("generateHashSHA256") {
it("takes data and returns a SHA256 digest") {
let string = "The quick brown fox jumps over the lazy dog"
let expectedHash = "D7A8FBB307D7809469CA9ABCB0082E4F8D5651E46D3CDB762D02D0BF37C9E592" //hex
let stringData = string.data(using: .utf8)!
let result = ARTCrypto.generateHashSHA256(stringData)
expect(result.hexString).to(equal(expectedHash))
}
}
context("encrypt") {
it("should generate a new IV every time it's called, and should be the first block encrypted") {
let params = ARTCipherParams(algorithm: "aes", key: key as ARTCipherKeyCompatible)
let cipher = ARTCrypto.cipher(with: params)
let data = "data".data(using: String.Encoding.utf8)!
var distinctOutputs = Set<NSData>()
var output: NSData?
for _ in 0..<3 {
cipher.encrypt(data, output:&output)
distinctOutputs.insert(output!)
let firstBlock = output!.subdata(with: NSMakeRange(0, Int((cipher as! ARTCbcCipher).blockLength)))
let paramsWithIV = ARTCipherParams(algorithm: "aes", key: key as ARTCipherKeyCompatible, iv: firstBlock)
var sameOutput: NSData?
ARTCrypto.cipher(with: paramsWithIV).encrypt(data, output:&sameOutput)
expect(output!).to(equal(sameOutput!))
}
expect(distinctOutputs.count).to(equal(3))
}
}
for cryptoFixture in CryptoTest.fixtures {
context("with fixtures from \(cryptoFixture.fileName).json") {
let (key, iv, items) = AblyTests.loadCryptoTestData(cryptoFixture.fileName)
let decoder = ARTDataEncoder.init(cipherParams: nil, error: nil)
let cipherParams = ARTCipherParams(algorithm: "aes", key: key as ARTCipherKeyCompatible, iv: iv)
let encrypter = ARTDataEncoder.init(cipherParams: cipherParams, error: nil)
func extractMessage(_ fixture: AblyTests.CryptoTestItem.TestMessage) -> ARTMessage {
let msg = ARTMessage(name: fixture.name, data: fixture.data)
msg.encoding = fixture.encoding
return msg
}
it("should encrypt messages as expected in the fixtures") {
for item in items {
let fixture = extractMessage(item.encoded)
let encryptedFixture = extractMessage(item.encrypted)
expect(encryptedFixture.encoding).to(endWith("\(cryptoFixture.expectedEncryptedEncoding)/base64"))
var error: NSError?
let decoded = fixture.decode(with: decoder, error: &error) as! ARTMessage
expect(error).to(beNil())
expect(decoded).notTo(beNil())
let encrypted = decoded.encode(with: encrypter, error: &error)
expect(error).to(beNil())
expect(encrypted).notTo(beNil())
expect((encrypted as! ARTMessage)).to(equal(encryptedFixture))
}
}
it("should decrypt messages as expected in the fixtures") {
for item in items {
let fixture = extractMessage(item.encoded)
let encryptedFixture = extractMessage(item.encrypted)
expect(encryptedFixture.encoding).to(endWith("\(cryptoFixture.expectedEncryptedEncoding)/base64"))
var error: NSError?
let decoded = fixture.decode(with: decoder, error: &error) as! ARTMessage
expect(error).to(beNil())
expect(decoded).notTo(beNil())
let decrypted = encryptedFixture.decode(with: encrypter, error: &error)
expect(error).to(beNil())
expect(decrypted).notTo(beNil())
expect((decrypted as! ARTMessage)).to(equal(decoded))
}
}
}
}
}
}
}