This repository has been archived by the owner. It is now read-only.
Permalink
Cannot retrieve contributors at this time
executable file
258 lines (224 sloc)
11.5 KB
| /* This Source Code Form is subject to the terms of the Mozilla Public | |
| * License, v. 2.0. If a copy of the MPL was not distributed with this | |
| * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | |
| @testable import Account | |
| import XCTest | |
| class PushCryptoTests: XCTestCase { | |
| let push = PushCrypto.sharedInstance | |
| func testRoundtrip_base64urlSafe() { | |
| let ogString = "I am the walrus! I am the eggman!" | |
| let ogData = ogString.data(using: .utf8)! | |
| guard let encodedString = ogData.base64urlSafeEncodedString else { | |
| return XCTFail("base64urlSafeEncodedString failed") | |
| } | |
| guard let decodedData = encodedString.base64urlSafeDecodedData else { | |
| return XCTFail("base64urlSafeDecodedData failed") | |
| } | |
| guard let decodedString = decodedData.utf8EncodedString else { | |
| return XCTFail("utf8EncodedString failed") | |
| } | |
| XCTAssertEqual(decodedString, ogString) | |
| } | |
| func testRoundtrip_base64urlSafe_withSpecialChars() { | |
| let encoded = "mW-ti1CqLQK4PyZBKy4q7g" | |
| XCTAssertEqual(encoded.base64urlSafeDecodedData!.base64urlSafeEncodedString, encoded) | |
| } | |
| struct Aes128GcmTest { | |
| let payload: String | |
| let recvPrivKey: String | |
| let authSecret: String | |
| let plaintext: String | |
| } | |
| func testDecrypt_aes128gcm() { | |
| let tests = [ | |
| Aes128GcmTest( | |
| payload: "SVzmyN6TpFOehi6GNJk8uwAAABhBBDwzeKLAq5VOFJhxjoXwi7cj-30l4TWmY_44WITrgZIza_kKVO1yDxwEXAtAXpu8OiFCsWyJCGc0w3Trr3CZ5kJ-LTLIraUBhwPFSxC0geECfXIJ2Ma0NVP6Ezr6WX8t3EWluoFAlE5kkLuNbZm6HQLmDZX0jOZER3wXIx2VuXpPld0", | |
| recvPrivKey: "yJnRHTLit-b-dJh4b1DyO5is5Tl60mHeObpkSezBLK0", | |
| authSecret: "mW-ti1CqLQK4PyZBKy4q7g", | |
| plaintext: "I am the walrus" | |
| ), | |
| Aes128GcmTest( | |
| payload: "DGv6ra1nlYgDCS1FRnbzlwAAEABBBP4z9KsN6nGRTbVYI_c7VJSPQTBtkgcy27mlmlMoZIIgDll6e3vCYLocInmYWAmS6TlzAC8wEqKK6PBru3jl7A_yl95bQpu6cVPTpK4Mqgkf1CXztLVBSt2Ks3oZwbuwXPXLWyouBWLVWGNWQexSgSxsj_Qulcy4a-fN", | |
| recvPrivKey: "q1dXpw3UpT5VOmu_cf_v6ih07Aems3njxI-JWgLcM94", | |
| authSecret: "BTBZMqHH6r4Tts7J_aSIgg", | |
| plaintext: "When I grow up, I want to be a watermelon" | |
| ), | |
| ] | |
| for test in tests { | |
| if let deciphered = try? push.aes128gcm(payload: test.payload, | |
| decryptWith: test.recvPrivKey, | |
| authenticateWith: test.authSecret) { | |
| XCTAssertEqual(deciphered, test.plaintext) | |
| } else { | |
| XCTFail("Failed to decipher \(test.plaintext)") | |
| } | |
| } | |
| } | |
| func testFail_aes128gcm() { | |
| let tests = [ | |
| Aes128GcmTest( | |
| payload: "bad base 64 encoding", | |
| recvPrivKey: "yJnRHTLit-b-dJh4b1DyO5is5Tl60mHeObpkSezBLK0", | |
| authSecret: "mW-ti1CqLQK4PyZBKy4q7g", | |
| plaintext: "bad base64" | |
| ), | |
| Aes128GcmTest( | |
| payload: "DGv6ra1nlYgDCS1FRnbzlwAAEABBBP4z9KsN6nGRTbVYI_c7VJSPQTBtkgcy27mlmlMoZIIgDll6e3vCYLocInmYWAmS6TlzAC8wEqKK6PBru3jl7A_yl95bQpu6cVPTpK4Mqgkf1CXztLVBSt2Ks3oZwbuwXPXLWyouBWLVWGNWQexSgSxsj_Qulcy4a-fN", | |
| recvPrivKey: "badkey", | |
| authSecret: "BTBZMqHH6r4Tts7J_aSIgg", | |
| plaintext: "bad key" | |
| ), | |
| ] | |
| for test in tests { | |
| do { | |
| _ = try push.aes128gcm(payload: test.payload, | |
| decryptWith: test.recvPrivKey, | |
| authenticateWith: test.authSecret) | |
| XCTFail("Somehow, deciphered \(test.plaintext)") | |
| } catch PushCryptoError.base64DecodeError { | |
| XCTAssertEqual(test.plaintext, tests[0].plaintext) | |
| } catch PushCryptoError.decryptionError(_) { | |
| XCTAssertEqual(test.plaintext, tests[1].plaintext) | |
| } catch PushCryptoError.utf8EncodingError { | |
| XCTFail("Not tested") | |
| } catch { | |
| XCTFail("Not tested but found \(error)") | |
| } | |
| } | |
| } | |
| func testRoundTrip_aes128gcm() { | |
| // The real reason for this test is to test generateKeys() | |
| guard let keys = try? push.generateKeys() else { | |
| return XCTFail("Generating keys should not fail") | |
| } | |
| let plaintext = "I am the egg man!" | |
| guard let payload = try? push.aes128gcm(plaintext: plaintext, encryptWith: keys.p256dhPublicKey, authenticateWith: keys.auth, rs: 1024, padLen: 2) else { | |
| return XCTFail("Encryption should not fail") | |
| } | |
| guard let deciphered = try? push.aes128gcm(payload: payload, decryptWith: keys.p256dhPrivateKey, authenticateWith: keys.auth) else { | |
| return XCTFail("Decryption should not fail") | |
| } | |
| XCTAssertEqual(deciphered, plaintext) | |
| } | |
| struct AesGcmTest { | |
| let plaintext: String | |
| let recvPrivKey: String | |
| let authSecret: String | |
| let ciphertext: String | |
| let cryptoKey: String | |
| let encryption: String | |
| } | |
| func testDecrypt_aesgcm() { | |
| let tests = [ | |
| AesGcmTest( | |
| plaintext: "I am the walrus", | |
| recvPrivKey: "9FWl15_QUQAWDaD3k3l50ZBZQJ4au27F1V4F0uLSD_M", | |
| authSecret: "R29vIGdvbyBnJyBqb29iIQ", | |
| ciphertext: "6nqAQUME8hNqw5J3kl8cpVVJylXKYqZOeseZG8UueKpA", | |
| cryptoKey: "keyid=\"dhkey\"; dh=\"BNoRDbb84JGm8g5Z5CFxurSqsXWJ11ItfXEWYVLE85Y7CYkDjXsIEc4aqxYaQ1G8BqkXCJ6DPpDrWtdWj_mugHU\"", | |
| encryption: "keyid=\"dhkey\"; salt=\"lngarbyKfMoi9Z75xYXmkg\"" | |
| ), | |
| AesGcmTest( | |
| plaintext: "Small record size", | |
| recvPrivKey: "4h23G_KkXC9TvBSK2v0Q7ImpS2YAuRd8hQyN0rFAwBg", | |
| authSecret: "g2rWVHUCpUxgcL9Tz7vyeQ", | |
| ciphertext: "oY4e5eDatDVt2fpQylxbPJM-3vrfhDasfPc8Q1PWt4tPfMVbz_sDNL_cvr0DXXkdFzS1lxsJsj550USx4MMl01ihjImXCjrw9R5xFgFrCAqJD3GwXA1vzS4T5yvGVbUp3SndMDdT1OCcEofTn7VC6xZ-zP8rzSQfDCBBxmPU7OISzr8Z4HyzFCGJeBfqiZ7yUfNlKF1x5UaZ4X6iU_TXx5KlQy_toV1dXZ2eEAMHJUcSdArvB6zRpFdEIxdcHcJyo1BIYgAYTDdAIy__IJVCPY_b2CE5W_6ohlYKB7xDyH8giNuWWXAgBozUfScLUVjPC38yJTpAUi6w6pXgXUWffende5FreQpnMFL1L4G-38wsI_-ISIOzdO8QIrXHxmtc1S5xzYu8bMqSgCinvCEwdeGFCmighRjj8t1zRWo0D14rHbQLPR_b1P5SvEeJTtS9Nm3iibM", | |
| cryptoKey: "dh=BCg6ZIGuE2ZNm2ti6Arf4CDVD_8--aLXAGLYhpghwjl1xxVjTLLpb7zihuEOGGbyt8Qj0_fYHBP4ObxwJNl56bk", | |
| encryption: "salt=5LIDBXbvkBvvb7ZdD-T4PQ; rs=3" | |
| ), | |
| AesGcmTest( | |
| plaintext: "Yet another message", | |
| recvPrivKey: "4h23G_KkXC9TvBSK2v0Q7ImpS2YAuRd8hQyN0rFAwBg", | |
| authSecret: "6plwZnSpVUbF7APDXus3UQ", | |
| ciphertext: "uEC5B_tR-fuQ3delQcrzrDCp40W6ipMZjGZ78USDJ5sMj-6bAOVG3AK6JqFl9E6AoWiBYYvMZfwThVxmDnw6RHtVeLKFM5DWgl1EwkOohwH2EhiDD0gM3io-d79WKzOPZE9rDWUSv64JstImSfX_ADQfABrvbZkeaWxh53EG59QMOElFJqHue4dMURpsMXg", | |
| cryptoKey: "dh=BEaA4gzA3i0JDuirGhiLgymS4hfFX7TNTdEhSk_HBlLpkjgCpjPL5c-GL9uBGIfa_fhGNKKFhXz1k9Kyens2ZpQ", | |
| encryption: "salt=ZFhzj0S-n29g9P2p4-I7tA; rs=8" | |
| ), | |
| AesGcmTest( | |
| plaintext: "Some message", | |
| recvPrivKey: "4h23G_KkXC9TvBSK2v0Q7ImpS2YAuRd8hQyN0rFAwBg", | |
| authSecret: "aTDc6JebzR6eScy2oLo4RQ", | |
| ciphertext: "Oo34w2F9VVnTMFfKtdx48AZWQ9Li9M6DauWJVgXU", | |
| cryptoKey: "dh=BCHFVrflyxibGLlgztLwKelsRZp4gqX3tNfAKFaxAcBhpvYeN1yIUMrxaDKiLh4LNKPtj0BOXGdr-IQ-QP82Wjo", | |
| encryption: "salt=zCU18Rw3A5aB_Xi-vfixmA; rs=24" | |
| ) | |
| ] | |
| for test in tests { | |
| if let deciphered = try? push.aesgcm(ciphertext: test.ciphertext, | |
| withHeaders: PushCryptoHeaders(encryption: test.encryption, cryptoKey: test.cryptoKey), | |
| decryptWith: test.recvPrivKey, | |
| authenticateWith: test.authSecret) { | |
| XCTAssertEqual(deciphered, test.plaintext) | |
| } else { | |
| XCTFail("Failed to decipher \(test.plaintext)") | |
| } | |
| } | |
| } | |
| func testFail_aesgcm() { | |
| let tests = [ | |
| AesGcmTest( | |
| plaintext: "bad base64", | |
| recvPrivKey: "4h23G_KkXC9TvBSK2v0Q7ImpS2YAuRd8hQyN0rFAwBg", | |
| authSecret: "aTDc6JebzR6eScy2oLo4RQ", | |
| ciphertext: "non base64", | |
| cryptoKey: "dh=BCHFVrflyxibGLlgztLwKelsRZp4gqX3tNfAKFaxAcBhpvYeN1yIUMrxaDKiLh4LNKPtj0BOXGdr-IQ-QP82Wjo", | |
| encryption: "salt=zCU18Rw3A5aB_Xi-vfixmA; rs=24" | |
| ), | |
| AesGcmTest( | |
| plaintext: "bad key", | |
| recvPrivKey: "badkey", | |
| authSecret: "aTDc6JebzR6eScy2oLo4RQ", | |
| ciphertext: "Oo34w2F9VVnTMFfKtdx48AZWQ9Li9M6DauWJVgXU", | |
| cryptoKey: "dh=BCHFVrflyxibGLlgztLwKelsRZp4gqX3tNfAKFaxAcBhpvYeN1yIUMrxaDKiLh4LNKPtj0BOXGdr-IQ-QP82Wjo", | |
| encryption: "salt=zCU18Rw3A5aB_Xi-vfixmA; rs=24" | |
| ), | |
| ] | |
| for test in tests { | |
| do { | |
| _ = try push.aesgcm(ciphertext: test.ciphertext, | |
| withHeaders: PushCryptoHeaders(encryption: test.encryption, cryptoKey: test.cryptoKey), | |
| decryptWith: test.recvPrivKey, | |
| authenticateWith: test.authSecret) | |
| XCTFail("Somehow, deciphered \(test.plaintext)") | |
| } catch PushCryptoError.base64DecodeError { | |
| XCTAssertEqual(test.plaintext, tests[0].plaintext) | |
| } catch PushCryptoError.decryptionError(_) { | |
| XCTAssertEqual(test.plaintext, tests[1].plaintext) | |
| } catch PushCryptoError.utf8EncodingError { | |
| XCTFail("Not tested") | |
| } catch { | |
| XCTFail("Not tested but found \(error)") | |
| } | |
| } | |
| } | |
| func testRoundTrip_aesgcm() { | |
| guard let keys = try? push.generateKeys() else { | |
| return XCTFail("Generating keys should not fail") | |
| } | |
| let plaintext = "While I'm away, I'll write home every day" | |
| guard let (headers, ciphertext) = try? push.aesgcm(plaintext: plaintext, | |
| encryptWith: keys.p256dhPublicKey, | |
| authenticateWith: keys.auth, | |
| rs: 28, | |
| padLen: 16) else { | |
| return XCTFail("Encryption should not fail") | |
| } | |
| guard let deciphered = try? push.aesgcm(ciphertext: ciphertext, | |
| withHeaders: headers, | |
| decryptWith: keys.p256dhPrivateKey, | |
| authenticateWith: keys.auth) else { | |
| return XCTFail("Decryption should not fail") | |
| } | |
| XCTAssertEqual(deciphered, plaintext) | |
| } | |
| func testKeyGeneration() { | |
| guard let keys = try? push.generateKeys() else { | |
| return XCTFail("Keys can't be tested") | |
| } | |
| // We can't really test if the keys are random enough, but we can check if they | |
| // roundtrip with the base64urlSafe encoding. | |
| for string in [keys.p256dhPublicKey, keys.p256dhPrivateKey, keys.auth] { | |
| XCTAssertEqual(string.base64urlSafeDecodedData?.base64urlSafeEncodedString, string) | |
| } | |
| } | |
| } |