diff --git a/.github/workflows/swift.yml b/.github/workflows/swift.yml index aa96dfe..bf22c5c 100644 --- a/.github/workflows/swift.yml +++ b/.github/workflows/swift.yml @@ -19,7 +19,3 @@ jobs: xcode-version: latest-stable - name: Build run: swift build -v - - name: Test - run: swift test --enable-code-coverage -v - - name: Benchmark - run: swift Benchmarks/Benchmark.swift diff --git a/.gitignore b/.gitignore index 28e118b..e281c0f 100644 --- a/.gitignore +++ b/.gitignore @@ -90,3 +90,6 @@ fastlane/test_output .DS_Store /.cache/ .idea +/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata +/.swiftpm/xcode/xcshareddata/xcschemes/OpenTDFKit.xcscheme +/.swiftpm/xcode/xcshareddata/xcschemes/OpenTDFKitTests.xcscheme diff --git a/Benchmarks/Benchmark.swift b/Benchmarks/Benchmark.swift index eaa633c..697f7b8 100644 --- a/Benchmarks/Benchmark.swift +++ b/Benchmarks/Benchmark.swift @@ -14,7 +14,7 @@ public struct Benchmark { public func run() -> (name: String, averageTime: Double) { var totalTime: Double = 0 - for _ in 1...iterations { + for _ in 1 ... iterations { let start = DispatchTime.now() operation() let end = DispatchTime.now() @@ -41,15 +41,15 @@ public func runBenchmarks(_ benchmarks: [Benchmark]) { // Example usage: let benchmarks = [ Benchmark(name: "Array Sorting") { - var arr = (1...1000).map { _ in Int.random(in: 1...1000) } + var arr = (1 ... 1000).map { _ in Int.random(in: 1 ... 1000) } arr.sort() }, Benchmark(name: "String Concatenation", iterations: 10000) { var str = "" - for _ in 1...100 { + for _ in 1 ... 100 { str += "Hello, World! " } - } + }, ] runBenchmarks(benchmarks) diff --git a/Info.plist b/Info.plist new file mode 100644 index 0000000..ad9feb4 --- /dev/null +++ b/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleIdentifier + + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + NSExceptionDomains + + localhost + + NSExceptionAllowsInsecureHTTPLoads + + NSExceptionRequiresForwardSecrecy + + NSIncludesSubdomains + + + + + + + diff --git a/OpenTDFKit/BinaryParser.swift b/OpenTDFKit/BinaryParser.swift index a5755ec..d5b00b7 100644 --- a/OpenTDFKit/BinaryParser.swift +++ b/OpenTDFKit/BinaryParser.swift @@ -138,20 +138,21 @@ public class BinaryParser { } func readPolicyBinding(bindingMode: PolicyBindingConfig) -> Data? { - var bindingSize: Int + let bindingSize // print("bindingMode", bindingMode) - if bindingMode.ecdsaBinding { + = if bindingMode.ecdsaBinding + { switch bindingMode.curve { case .secp256r1, .xsecp256k1: - bindingSize = 64 + 64 case .secp384r1: - bindingSize = 96 + 96 case .secp521r1: - bindingSize = 132 + 132 } } else { // GMAC Tag Binding - bindingSize = 16 + 16 } // print("bindingSize", bindingSize) return read(length: bindingSize) @@ -180,7 +181,7 @@ public class BinaryParser { public func parseHeader() throws -> Header { // print("Starting to parse header") - + guard let magicNumber = read(length: FieldSize.magicNumberSize) else { throw ParsingError.invalidFormat } @@ -188,7 +189,7 @@ public class BinaryParser { guard magicNumber == Header.magicNumber else { throw ParsingError.invalidMagicNumber } - + guard let versionData = read(length: FieldSize.versionSize) else { throw ParsingError.invalidFormat } diff --git a/OpenTDFKit/KASRest.swift b/OpenTDFKit/KASRest.swift index 2e6404b..dc0c360 100644 --- a/OpenTDFKit/KASRest.swift +++ b/OpenTDFKit/KASRest.swift @@ -9,7 +9,7 @@ class KASRest { self.apiKey = apiKey } - public func rewrap(key: String, completion: @escaping (Result) -> Void) { + public func rewrap(key: String, completion: @escaping @Sendable (Result) -> Void) { guard let url = URL(string: "\(baseURL)/rewrap") else { completion(.failure(NSError(domain: "KASClient", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid URL"]))) return diff --git a/OpenTDFKit/KASWebSocket.swift b/OpenTDFKit/KASWebSocket.swift index f3a6f1b..6e62ae4 100644 --- a/OpenTDFKit/KASWebSocket.swift +++ b/OpenTDFKit/KASWebSocket.swift @@ -1,6 +1,6 @@ +import Combine import CryptoKit import Foundation -import Combine public enum WebSocketConnectionState { case disconnected @@ -18,7 +18,7 @@ extension WebSocketConnectionState: CustomStringConvertible { } } -public class KASWebSocket { +public class KASWebSocket: @unchecked Sendable { private var webSocketTask: URLSessionWebSocketTask? private var urlSession: URLSession? private let myPrivateKey: P256.KeyAgreement.PrivateKey! @@ -29,7 +29,7 @@ public class KASWebSocket { private var customMessageCallback: ((Data) -> Void)? private let kasUrl: URL private let token: String - + private let connectionStateSubject = CurrentValueSubject(.disconnected) public var connectionStatePublisher: AnyPublisher { connectionStateSubject.eraseToAnyPublisher() @@ -53,17 +53,17 @@ public class KASWebSocket { public func setCustomMessageCallback(_ callback: @escaping (Data) -> Void) { customMessageCallback = callback } - - public func sendCustomMessage(_ message: Data, completion: @escaping (Error?) -> Void) { + + public func sendCustomMessage(_ message: Data, completion: @Sendable @escaping (Error?) -> Void) { let task = URLSessionWebSocketTask.Message.data(message) webSocketTask?.send(task) { error in - if let error = error { + if let error { print("Error sending custom message: \(error)") } completion(error) } } - + public func connect() { connectionStateSubject.send(.connecting) // Create a URLRequest object with the WebSocket URL @@ -87,7 +87,7 @@ public class KASWebSocket { private func pingPeriodically() { webSocketTask?.sendPing { [weak self] error in - if let error = error { + if let error { print("Error sending ping: \(error)") self?.connectionStateSubject.send(.disconnected) } else { @@ -276,16 +276,15 @@ public class KASWebSocket { } } - public func sendPing(completionHandler: @escaping (Error?) -> Void) { + public func sendPing(completionHandler: @escaping @Sendable (Error?) -> Void) { webSocketTask?.sendPing { error in - if let error = error { + if let error { print("Error sending ping: \(error)") } completionHandler(error) } } - public func disconnect() { webSocketTask?.cancel(with: .goingAway, reason: nil) connectionStateSubject.send(.disconnected) @@ -334,7 +333,7 @@ struct RewrapMessage { struct RewrappedKeyMessage { let messageType: Data = .init([0x04]) let rewrappedKey: Data - + func toData() -> Data { var data = Data() data.append(messageType) diff --git a/OpenTDFKit/NanoTDF.swift b/OpenTDFKit/NanoTDF.swift index 1b5770b..a6c821e 100644 --- a/OpenTDFKit/NanoTDF.swift +++ b/OpenTDFKit/NanoTDF.swift @@ -1,17 +1,17 @@ import CryptoKit import Foundation -public struct NanoTDF { +public struct NanoTDF: Sendable { public var header: Header public var payload: Payload public var signature: Signature? - + public init(header: Header, payload: Payload, signature: Signature? = nil) { self.header = header self.payload = payload self.signature = signature } - + public func toData() -> Data { var data = Data() data.append(header.toData()) @@ -32,7 +32,7 @@ public struct NanoTDF { } } -public struct Header { +public struct Header: Sendable { public static let magicNumber = Data([0x4C, 0x31]) // 0x4C31 (L1L) - first 18 bits public static let version: UInt8 = 0x4C // "L" public let kas: ResourceLocator @@ -62,7 +62,7 @@ public struct Header { } } -public struct Payload { +public struct Payload: Sendable { public let length: UInt32 public let iv: Data public let ciphertext: Data @@ -87,7 +87,7 @@ public struct Payload { } } -public struct Signature { +public struct Signature: Sendable { let publicKey: Data let signature: Data @@ -99,7 +99,7 @@ public struct Signature { } } -public struct PolicyBindingConfig { +public struct PolicyBindingConfig: Sendable { // true ECDSA using creator key. The signature is used as the binding // false GMAC tag is computed over the policy body using the derived symmetric key. var ecdsaBinding: Bool @@ -115,7 +115,7 @@ public struct PolicyBindingConfig { } } -public struct SignatureAndPayloadConfig { +public struct SignatureAndPayloadConfig: Sendable { var signed: Bool var signatureCurve: Curve? let payloadCipher: Cipher? @@ -136,7 +136,7 @@ public struct SignatureAndPayloadConfig { } } -public enum ProtocolEnum: UInt8 { +public enum ProtocolEnum: UInt8, Sendable { case http = 0x00 case https = 0x01 // BEGIN out-of-spec @@ -146,9 +146,9 @@ public enum ProtocolEnum: UInt8 { case sharedResourceDirectory = 0xFF } -public struct ResourceLocator { - let protocolEnum: ProtocolEnum - let body: String +public struct ResourceLocator: Sendable { + public let protocolEnum: ProtocolEnum + public let body: String public init?(protocolEnum: ProtocolEnum, body: String) { guard body.utf8.count >= 1, body.utf8.count <= 255 else { @@ -170,8 +170,8 @@ public struct ResourceLocator { } } -public struct Policy { - public enum PolicyType: UInt8 { +public struct Policy: Sendable { + public enum PolicyType: UInt8, Sendable { case remote = 0x00 case embeddedPlaintext = 0x01 case embeddedEncrypted = 0x02 @@ -179,11 +179,11 @@ public struct Policy { case embeddedEncryptedWithPolicyKeyAccess = 0x03 } - let type: PolicyType - let body: EmbeddedPolicyBody? - let remote: ResourceLocator? - var binding: Data? - + public let type: PolicyType + public let body: EmbeddedPolicyBody? + public let remote: ResourceLocator? + public var binding: Data? + public init(type: PolicyType, body: EmbeddedPolicyBody?, remote: ResourceLocator?, binding: Data? = nil) { self.type = type self.body = body @@ -211,10 +211,10 @@ public struct Policy { } } -public struct EmbeddedPolicyBody { - let length: Int - let body: Data - let keyAccess: PolicyKeyAccess? +public struct EmbeddedPolicyBody: Sendable { + public let length: Int + public let body: Data + public let keyAccess: PolicyKeyAccess? func toData() -> Data { var data = Data() @@ -227,9 +227,9 @@ public struct EmbeddedPolicyBody { } } -public struct PolicyKeyAccess { - let resourceLocator: ResourceLocator - let ephemeralPublicKey: Data +public struct PolicyKeyAccess: Sendable { + public let resourceLocator: ResourceLocator + public let ephemeralPublicKey: Data func toData() -> Data { var data = Data() @@ -239,7 +239,7 @@ public struct PolicyKeyAccess { } } -public enum Curve: UInt8 { +public enum Curve: UInt8, Sendable { case secp256r1 = 0x00 case secp384r1 = 0x01 case secp521r1 = 0x02 @@ -248,7 +248,7 @@ public enum Curve: UInt8 { // END in-spec unsupported } -public enum Cipher: UInt8 { +public enum Cipher: UInt8, Sendable { case aes256GCM64 = 0x00 case aes256GCM96 = 0x01 case aes256GCM104 = 0x02 @@ -408,7 +408,7 @@ public func createNanoTDF(kas: KasMetadata, policy: inout Policy, plaintext: Dat policy: policy, ephemeralPublicKey: ephemeralPublicKeyData ) - + return NanoTDF(header: header, payload: payload, signature: nil) diff --git a/OpenTDFKit/NanoTDFManager.swift b/OpenTDFKit/NanoTDFManager.swift index d509d12..2c910db 100644 --- a/OpenTDFKit/NanoTDFManager.swift +++ b/OpenTDFKit/NanoTDFManager.swift @@ -24,10 +24,10 @@ class NanoTDFManager { } func isEmpty() -> Bool { - return nanoTDFs.isEmpty + nanoTDFs.isEmpty } func getCount() -> Int { - return count + count } } diff --git a/OpenTDFKitTests/InitializationTests.swift b/OpenTDFKitTests/InitializationTests.swift index 94fcb71..f12c52a 100644 --- a/OpenTDFKitTests/InitializationTests.swift +++ b/OpenTDFKitTests/InitializationTests.swift @@ -28,15 +28,15 @@ final class InitializationTests: XCTestCase { // out of spec - too small var locator = ResourceLocator(protocolEnum: .http, body: "") XCTAssertNil(locator) - + // out of spec - too large let body256Bytes = String(repeating: "a", count: 256) locator = ResourceLocator(protocolEnum: .http, body: body256Bytes) XCTAssertNil(locator) - + locator = ResourceLocator(protocolEnum: .http, body: "localhost:8080") XCTAssertNotNil(locator) - + // Test valid header creation XCTAssertNoThrow(Header( kas: locator!, diff --git a/OpenTDFKitTests/KASWebsocketTests.swift b/OpenTDFKitTests/KASWebsocketTests.swift index 6bcbc2b..57a379d 100644 --- a/OpenTDFKitTests/KASWebsocketTests.swift +++ b/OpenTDFKitTests/KASWebsocketTests.swift @@ -52,11 +52,11 @@ final class KASWebsocketTests: XCTestCase { let kasMetadata = KasMetadata(resourceLocator: kasRL!, publicKey: publicKey, curve: .secp256r1) let remotePolicy = ResourceLocator(protocolEnum: .sharedResourceDirectory, body: "5Cqk3ERPToSMuY8UoKJtcmo4fs1iVyQpq6ndzWzpzWezAF1W") var policy = Policy(type: .remote, body: nil, remote: remotePolicy, binding: nil) - + do { - var i = 0; + var i = 0 while i < 2000 { - i += 1; + i += 1 // create let nanoTDF = try createNanoTDF(kas: kasMetadata, policy: &policy, plaintext: plaintext) // print("Encryption successful") @@ -65,7 +65,7 @@ final class KASWebsocketTests: XCTestCase { nanoTDFManager.addNanoTDF(nanoTDF, withIdentifier: id) webSocket.sendRewrapMessage(header: nanoTDF.header) } - + } catch { print("Error creating nanoTDF: \(error)") } diff --git a/OpenTDFKitTests/NanoTDFSymmetricTests.swift b/OpenTDFKitTests/NanoTDFSymmetricTests.swift index b3a08d7..f269647 100644 --- a/OpenTDFKitTests/NanoTDFSymmetricTests.swift +++ b/OpenTDFKitTests/NanoTDFSymmetricTests.swift @@ -10,7 +10,7 @@ import XCTest // #if DEBUG // var storedKey: SymmetricKey? // #endif -//class SymmetricKeyTests: XCTestCase { +// class SymmetricKeyTests: XCTestCase { // let originalMessage = "This is a secret message for TDF testing." // // Simulating key storage // static var storedKey: SymmetricKey? @@ -97,4 +97,4 @@ import XCTest // // Clean up: delete the file // try FileManager.default.removeItem(at: fileURL) // } -//} +// } diff --git a/OpenTDFKitTests/OpenTDFKitTests.swift b/OpenTDFKitTests/OpenTDFKitTests.swift index f27a5db..367655a 100644 --- a/OpenTDFKitTests/OpenTDFKitTests.swift +++ b/OpenTDFKitTests/OpenTDFKitTests.swift @@ -3,7 +3,7 @@ import XCTest final class OpenTDFKitTests: XCTestCase { // Test for KASClient's rewrap function - func testKASClientRewrap() { + @MainActor func testKASClientRewrap() { let kasClient = KASRest(baseURL: "https://platform.virtru.us/api/kas", apiKey: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c") let expectation = expectation(description: "Rewrap key") diff --git a/Package.swift b/Package.swift index 15de73a..4d21eb6 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.10 +// swift-tools-version:6.0 import PackageDescription let package = Package(