170 lines
8.7 KiB
Swift
170 lines
8.7 KiB
Swift
import Foundation
|
|
import XCTest
|
|
import CryptoKit
|
|
@testable import SecretKit
|
|
@testable import SecretAgentKit
|
|
|
|
class AgentTests: XCTestCase {
|
|
|
|
let stubWriter = StubFileHandleWriter()
|
|
|
|
// MARK: Identity Listing
|
|
|
|
func testEmptyStores() {
|
|
let stubReader = StubFileHandleReader(availableData: Constants.Requests.requestIdentities)
|
|
let agent = Agent(storeList: SecretStoreList())
|
|
agent.handle(reader: stubReader, writer: stubWriter)
|
|
XCTAssertEqual(stubWriter.data, Constants.Responses.requestIdentitiesEmpty)
|
|
}
|
|
|
|
func testIdentitiesList() {
|
|
let stubReader = StubFileHandleReader(availableData: Constants.Requests.requestIdentities)
|
|
let list = storeList(with: [Constants.Secrets.ecdsa256Secret, Constants.Secrets.ecdsa384Secret])
|
|
let agent = Agent(storeList: list)
|
|
agent.handle(reader: stubReader, writer: stubWriter)
|
|
XCTAssertEqual(stubWriter.data, Constants.Responses.requestIdentitiesMultiple)
|
|
}
|
|
|
|
// MARK: Signatures
|
|
|
|
func testNoMatchingIdentities() {
|
|
let stubReader = StubFileHandleReader(availableData: Constants.Requests.requestSignatureWithNoneMatching)
|
|
let list = storeList(with: [Constants.Secrets.ecdsa256Secret, Constants.Secrets.ecdsa384Secret])
|
|
let agent = Agent(storeList: list)
|
|
agent.handle(reader: stubReader, writer: stubWriter)
|
|
// XCTAssertEqual(stubWriter.data, Constants.Responses.requestFailure)
|
|
}
|
|
|
|
func testSignature() {
|
|
let stubReader = StubFileHandleReader(availableData: Constants.Requests.requestSignature)
|
|
let requestReader = OpenSSHReader(data: Constants.Requests.requestSignature[5...])
|
|
_ = requestReader.readNextChunk()
|
|
let dataToSign = requestReader.readNextChunk()
|
|
let list = storeList(with: [Constants.Secrets.ecdsa256Secret, Constants.Secrets.ecdsa384Secret])
|
|
let agent = Agent(storeList: list)
|
|
agent.handle(reader: stubReader, writer: stubWriter)
|
|
let outer = OpenSSHReader(data: stubWriter.data[5...])
|
|
let payload = outer.readNextChunk()
|
|
let inner = OpenSSHReader(data: payload)
|
|
_ = inner.readNextChunk()
|
|
let signedData = inner.readNextChunk()
|
|
let rsData = OpenSSHReader(data: signedData)
|
|
var r = rsData.readNextChunk()
|
|
var s = rsData.readNextChunk()
|
|
// This is fine IRL, but it freaks out CryptoKit
|
|
if r[0] == 0 {
|
|
r.removeFirst()
|
|
}
|
|
if s[0] == 0 {
|
|
s.removeFirst()
|
|
}
|
|
var rs = r
|
|
rs.append(s)
|
|
let signature = try! P256.Signing.ECDSASignature(rawRepresentation: rs)
|
|
let valid = try! P256.Signing.PublicKey(x963Representation: Constants.Secrets.ecdsa256Secret.publicKey).isValidSignature(signature, for: dataToSign)
|
|
XCTAssertTrue(valid)
|
|
}
|
|
|
|
// MARK: Witness protocol
|
|
|
|
func testWitnessObjectionStopsRequest() {
|
|
let stubReader = StubFileHandleReader(availableData: Constants.Requests.requestSignature)
|
|
let list = storeList(with: [Constants.Secrets.ecdsa256Secret])
|
|
let witness = StubWitness(speakNow: { _,_ in
|
|
return true
|
|
}, witness: { _, _ in })
|
|
let agent = Agent(storeList: list, witness: witness)
|
|
agent.handle(reader: stubReader, writer: stubWriter)
|
|
XCTAssertEqual(stubWriter.data, Constants.Responses.requestFailure)
|
|
}
|
|
|
|
func testWitnessSignature() {
|
|
let stubReader = StubFileHandleReader(availableData: Constants.Requests.requestSignature)
|
|
let list = storeList(with: [Constants.Secrets.ecdsa256Secret])
|
|
var witnessed = false
|
|
let witness = StubWitness(speakNow: { _, trace in
|
|
return false
|
|
}, witness: { _, trace in
|
|
witnessed = true
|
|
})
|
|
let agent = Agent(storeList: list, witness: witness)
|
|
agent.handle(reader: stubReader, writer: stubWriter)
|
|
XCTAssertTrue(witnessed)
|
|
}
|
|
|
|
func testRequestTracing() {
|
|
let stubReader = StubFileHandleReader(availableData: Constants.Requests.requestSignature)
|
|
let list = storeList(with: [Constants.Secrets.ecdsa256Secret])
|
|
var speakNowTrace: SigningRequestProvenance! = nil
|
|
var witnessTrace: SigningRequestProvenance! = nil
|
|
let witness = StubWitness(speakNow: { _, trace in
|
|
speakNowTrace = trace
|
|
return false
|
|
}, witness: { _, trace in
|
|
witnessTrace = trace
|
|
})
|
|
let agent = Agent(storeList: list, witness: witness)
|
|
agent.handle(reader: stubReader, writer: stubWriter)
|
|
XCTAssertEqual(witnessTrace, speakNowTrace)
|
|
XCTAssertEqual(witnessTrace.origin.displayName, "Finder")
|
|
XCTAssertEqual(witnessTrace.origin.validSignature, true)
|
|
XCTAssertEqual(witnessTrace.origin.parentPID, 1)
|
|
}
|
|
|
|
// MARK: Exception Handling
|
|
|
|
func testSignatureException() {
|
|
let stubReader = StubFileHandleReader(availableData: Constants.Requests.requestSignature)
|
|
let list = storeList(with: [Constants.Secrets.ecdsa256Secret, Constants.Secrets.ecdsa384Secret])
|
|
let store = list.stores.first?.base as! Stub.Store
|
|
store.shouldThrow = true
|
|
let agent = Agent(storeList: list)
|
|
agent.handle(reader: stubReader, writer: stubWriter)
|
|
XCTAssertEqual(stubWriter.data, Constants.Responses.requestFailure)
|
|
}
|
|
|
|
// MARK: Unsupported
|
|
|
|
func testUnhandledAdd() {
|
|
let stubReader = StubFileHandleReader(availableData: Constants.Requests.addIdentity)
|
|
let agent = Agent(storeList: SecretStoreList())
|
|
agent.handle(reader: stubReader, writer: stubWriter)
|
|
XCTAssertEqual(stubWriter.data, Constants.Responses.requestFailure)
|
|
}
|
|
|
|
}
|
|
|
|
extension AgentTests {
|
|
|
|
func storeList(with secrets: [Stub.Secret]) -> SecretStoreList {
|
|
let store = Stub.Store()
|
|
store.secrets.append(contentsOf: secrets)
|
|
let storeList = SecretStoreList()
|
|
storeList.add(store: store)
|
|
return storeList
|
|
}
|
|
|
|
enum Constants {
|
|
|
|
enum Requests {
|
|
static let requestIdentities = Data(base64Encoded: "AAAAAQs=")!
|
|
static let addIdentity = Data(base64Encoded: "AAAAARE=")!
|
|
static let requestSignatureWithNoneMatching = Data(base64Encoded: "AAABhA0AAACIAAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBEqCbkJbOHy5S1wVCaJoKPmpS0egM4frMqllgnlRRQ/Uvnn6EVS8oV03cPA2Bz0EdESyRKA/sbmn0aBtgjIwGELxu45UXEW1TEz6TxyS0u3vuIqR3Wo1CrQWRDnkrG/pBQAAAO8AAAAgbqmrqPUtJ8mmrtaSVexjMYyXWNqjHSnoto7zgv86xvcyAAAAA2dpdAAAAA5zc2gtY29ubmVjdGlvbgAAAAlwdWJsaWNrZXkBAAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAACIAAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBEqCbkJbOHy5S1wVCaJoKPmpS0egM4frMqllgnlRRQ/Uvnn6EVS8oV03cPA2Bz0EdESyRKA/sbmn0aBtgjIwGELxu45UXEW1TEz6TxyS0u3vuIqR3Wo1CrQWRDnkrG/pBQAAAAA=")!
|
|
static let requestSignature = Data(base64Encoded: "AAABRA0AAABoAAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBKzOkUiVJEcACMtAd9X7xalbc0FYZyhbmv2dsWl4IP2GWIi+RcsaHQNw+nAIQ8CKEYmLnl0VLDp5Ef8KMhgIy08AAADPAAAAIBIFsbCZ4/dhBmLNGHm0GKj7EJ4N8k/jXRxlyg+LFIYzMgAAAANnaXQAAAAOc3NoLWNvbm5lY3Rpb24AAAAJcHVibGlja2V5AQAAABNlY2RzYS1zaGEyLW5pc3RwMjU2AAAAaAAAABNlY2RzYS1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQSszpFIlSRHAAjLQHfV+8WpW3NBWGcoW5r9nbFpeCD9hliIvkXLGh0DcPpwCEPAihGJi55dFSw6eRH/CjIYCMtPAAAAAA==")!
|
|
}
|
|
|
|
enum Responses {
|
|
static let requestIdentitiesEmpty = Data(base64Encoded: "AAAABQwAAAAA")!
|
|
static let requestIdentitiesMultiple = Data(base64Encoded: "AAABKwwAAAACAAAAaAAAABNlY2RzYS1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQSszpFIlSRHAAjLQHfV+8WpW3NBWGcoW5r9nbFpeCD9hliIvkXLGh0DcPpwCEPAihGJi55dFSw6eRH/CjIYCMtPAAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAACIAAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBLKSzA5q3jCb3q0JKigvcxfWVGrJ+bklpG0Zc9YzUwrbsh9SipvlSJi+sHQI+O0m88DOpRBAtuAHX60euD/Yv250tovN7/+MEFbXGZ/hLdd0BoFpWbLfJcQj806KJGlcDAAAABNlY2RzYS1zaGEyLW5pc3RwMzg0")!
|
|
static let requestFailure = Data(base64Encoded: "AAAAAQU=")!
|
|
}
|
|
|
|
enum Secrets {
|
|
static let ecdsa256Secret = Stub.Secret(keySize: 256, publicKey: Data(base64Encoded: "BKzOkUiVJEcACMtAd9X7xalbc0FYZyhbmv2dsWl4IP2GWIi+RcsaHQNw+nAIQ8CKEYmLnl0VLDp5Ef8KMhgIy08=")!, privateKey: Data(base64Encoded: "BKzOkUiVJEcACMtAd9X7xalbc0FYZyhbmv2dsWl4IP2GWIi+RcsaHQNw+nAIQ8CKEYmLnl0VLDp5Ef8KMhgIy09nw780wy/TSfUmzj15iJkV234AaCLNl+H8qFL6qK8VIg==")!)
|
|
static let ecdsa384Secret = Stub.Secret(keySize: 384, publicKey: Data(base64Encoded: "BLKSzA5q3jCb3q0JKigvcxfWVGrJ+bklpG0Zc9YzUwrbsh9SipvlSJi+sHQI+O0m88DOpRBAtuAHX60euD/Yv250tovN7/+MEFbXGZ/hLdd0BoFpWbLfJcQj806KJGlcDA==")!, privateKey: Data(base64Encoded: "BLKSzA5q3jCb3q0JKigvcxfWVGrJ+bklpG0Zc9YzUwrbsh9SipvlSJi+sHQI+O0m88DOpRBAtuAHX60euD/Yv250tovN7/+MEFbXGZ/hLdd0BoFpWbLfJcQj806KJGlcDHNapAOzrt9E+9QC4/KYoXS7Uw4pmdAz53uIj02tttiq3c0ZyIQ7XoscWWRqRrz8Kw==")!)
|
|
}
|
|
|
|
}
|
|
|
|
}
|