├── capillaryslack ├── capillaryios-Bridging-Header.h ├── capillaryslack.h ├── EncryptedData.swift ├── Info.plist ├── Info-tvOS.plist ├── Message.swift ├── Signature.swift ├── Data+SHA.swift ├── PrivateKey.swift ├── EncryptedMessage.swift ├── Key.swift ├── capillaryios.swift ├── SwiftyRSAError.swift ├── PublicKey.swift ├── X509Certificate.swift ├── ClearMessage.swift ├── Asn1Parser.swift ├── SwiftyRSA+ObjC.swift ├── SwiftyRSA.swift └── StoredKey.swift ├── Podfile ├── .DS_Store ├── Podfile.lock ├── sampleapp ├── Assets.xcassets │ ├── Contents.json │ ├── AccentColor.colorset │ │ └── Contents.json │ └── AppIcon.appiconset │ │ └── Contents.json ├── Preview Content │ └── Preview Assets.xcassets │ │ └── Contents.json ├── sampleappApp.swift └── ContentView.swift ├── .idea ├── vcs.xml ├── misc.xml ├── .gitignore ├── modules.xml └── capillaryios.iml ├── capillaryslack.xcodeproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── project.pbxproj ├── capillaryslack.xcworkspace ├── contents.xcworkspacedata └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── capillaryslack.podspec ├── sampleappUITests ├── sampleappUITestsLaunchTests.swift └── sampleappUITests.swift ├── sampleappTests └── sampleappTests.swift ├── capillaryslackTests └── capillaryslackTests.swift └── .gitignore /capillaryslack/capillaryios-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | target 'capillaryslack' do 2 | use_frameworks! 3 | 4 | end 5 | -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oianmol/slack_capillary_ios/master/.DS_Store -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODFILE CHECKSUM: 420a22286b0c342fd762af9a25dd1bcc71bf13bd 2 | 3 | COCOAPODS: 1.12.0 4 | -------------------------------------------------------------------------------- /sampleapp/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /sampleapp/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Editor-based HTTP Client requests 5 | /httpRequests/ 6 | # Datasource local storage ignored files 7 | /dataSources/ 8 | /dataSources.local.xml 9 | -------------------------------------------------------------------------------- /capillaryslack.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /sampleapp/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /sampleapp/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "platform" : "ios", 6 | "size" : "1024x1024" 7 | } 8 | ], 9 | "info" : { 10 | "author" : "xcode", 11 | "version" : 1 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /capillaryslack.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /capillaryslack.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /sampleapp/sampleappApp.swift: -------------------------------------------------------------------------------- 1 | // 2 | // sampleappApp.swift 3 | // sampleapp 4 | // 5 | // Created by Anmol Verma on 02/12/22. 6 | // 7 | 8 | import SwiftUI 9 | 10 | @main 11 | struct sampleappApp: App { 12 | var body: some Scene { 13 | WindowGroup { 14 | ContentView() 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /capillaryslack.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/capillaryios.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /capillaryslack/capillaryslack.h: -------------------------------------------------------------------------------- 1 | // 2 | // capillaryslack.h 3 | // capillaryslack 4 | // 5 | // Created by Anmol Verma on 23/11/22. 6 | // 7 | 8 | #import 9 | 10 | //! Project version number for capillaryslack. 11 | FOUNDATION_EXPORT double capillaryslackVersionNumber; 12 | 13 | //! Project version string for capillaryslack. 14 | FOUNDATION_EXPORT const unsigned char capillaryslackVersionString[]; 15 | 16 | // In this header, you should import all the public headers of your framework using statements like #import 17 | 18 | 19 | -------------------------------------------------------------------------------- /capillaryslack/EncryptedData.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EncryptedData.swift 3 | // capillaryslack 4 | // 5 | // Created by Anmol Verma on 02/12/22. 6 | // 7 | 8 | import Foundation 9 | 10 | @objc public class EncryptedData: NSObject { 11 | 12 | @objc public private(set) var first: String? 13 | @objc public private(set) var second: String? 14 | 15 | private init(_ first: String?, _ second: String?) { 16 | super.init() 17 | self.first = first 18 | self.second = second 19 | } 20 | 21 | @objc public func firstItem() -> String? { 22 | return first 23 | } 24 | 25 | @objc public func secondItem() -> String? { 26 | return second 27 | } 28 | 29 | public convenience init(first: String?,second:String?) { 30 | self.init(first, second) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /capillaryslack.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | 3 | s.name = "capillaryslack" 4 | s.version = "1.0.0" 5 | s.summary = "Public key RSA encryption." 6 | s.static_framework = true 7 | s.description = <<-DESC 8 | Encrypt with a RSA public key, decrypt with a RSA private key. 9 | DESC 10 | 11 | s.homepage = "https://github.com/oianmol/slack_capillary_ios" 12 | s.license = "MIT" 13 | s.author = { "Anmol Verma" => "anmol.verma4@gmail.com" } 14 | 15 | s.source = { :git => "https://github.com/oianmol/slack_capillary_ios.git", :branch => "master" } 16 | s.source_files = "capillaryslack/*.{swift,m,h}" 17 | s.framework = "Security" 18 | s.requires_arc = true 19 | 20 | s.swift_version = "5.0" 21 | s.ios.deployment_target = "14.0" 22 | 23 | s.subspec "ObjC" do |sp| 24 | sp.source_files = "capillaryslack/*.{swift,m,h}" 25 | end 26 | end -------------------------------------------------------------------------------- /capillaryslack/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.7.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /sampleappUITests/sampleappUITestsLaunchTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // sampleappUITestsLaunchTests.swift 3 | // sampleappUITests 4 | // 5 | // Created by Anmol Verma on 02/12/22. 6 | // 7 | 8 | import XCTest 9 | 10 | final class sampleappUITestsLaunchTests: XCTestCase { 11 | 12 | override class var runsForEachTargetApplicationUIConfiguration: Bool { 13 | true 14 | } 15 | 16 | override func setUpWithError() throws { 17 | continueAfterFailure = false 18 | } 19 | 20 | func testLaunch() throws { 21 | let app = XCUIApplication() 22 | app.launch() 23 | 24 | // Insert steps here to perform after app launch but before taking a screenshot, 25 | // such as logging into a test account or navigating somewhere in the app 26 | 27 | let attachment = XCTAttachment(screenshot: app.screenshot()) 28 | attachment.name = "Launch Screen" 29 | attachment.lifetime = .keepAlways 30 | add(attachment) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /capillaryslack/Info-tvOS.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.7.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | UIRequiredDeviceCapabilities 26 | 27 | arm64 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /capillaryslack/Message.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Message.swift 3 | // SwiftyRSA 4 | // 5 | // Created by Loïs Di Qual on 9/19/16. 6 | // Copyright © 2016 Scoop. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public protocol Message { 12 | var data: Data { get } 13 | var base64String: String { get } 14 | init(data: Data) 15 | init(base64Encoded base64String: String) throws 16 | } 17 | 18 | public extension Message { 19 | 20 | /// Base64-encoded string of the message data 21 | var base64String: String { 22 | return data.base64EncodedString() 23 | } 24 | 25 | /// Creates an encrypted message with a base64-encoded string. 26 | /// 27 | /// - Parameter base64String: Base64-encoded data of the encrypted message 28 | /// - Throws: SwiftyRSAError 29 | init(base64Encoded base64String: String) throws { 30 | guard let data = Data(base64Encoded: base64String) else { 31 | throw SwiftyRSAError.invalidBase64String 32 | } 33 | self.init(data: data) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /sampleappTests/sampleappTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // sampleappTests.swift 3 | // sampleappTests 4 | // 5 | // Created by Anmol Verma on 02/12/22. 6 | // 7 | 8 | import XCTest 9 | @testable import sampleapp 10 | 11 | final class sampleappTests: XCTestCase { 12 | 13 | override func setUpWithError() throws { 14 | // Put setup code here. This method is called before the invocation of each test method in the class. 15 | } 16 | 17 | override func tearDownWithError() throws { 18 | // Put teardown code here. This method is called after the invocation of each test method in the class. 19 | } 20 | 21 | func testExample() throws { 22 | // This is an example of a functional test case. 23 | // Use XCTAssert and related functions to verify your tests produce the correct results. 24 | // Any test you write for XCTest can be annotated as throws and async. 25 | // Mark your test throws to produce an unexpected failure when your test encounters an uncaught error. 26 | // Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards. 27 | } 28 | 29 | func testPerformanceExample() throws { 30 | // This is an example of a performance test case. 31 | self.measure { 32 | // Put the code you want to measure the time of here. 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /sampleapp/ContentView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContentView.swift 3 | // sampleapp 4 | // 5 | // Created by Anmol Verma on 02/12/22. 6 | // 7 | 8 | import SwiftUI 9 | import capillaryslack 10 | 11 | struct ContentView: View { 12 | 13 | @State var str:String = "none" 14 | 15 | init() { 16 | CapillaryIOS.initNow(chainId: "anmol",isUnitTest: false) 17 | 18 | let publicKey = CapillaryIOS.publicKey(chainId: "anmol") 19 | let privateKey = CapillaryIOS.privateKey(chainId: "anmol")! 20 | 21 | let encrypted = CapillaryIOS.encrypt(data: "anmol".data(using: .utf8)!, publicKey: publicKey!) 22 | print("encrypted now") 23 | 24 | let decrypted = CapillaryIOS.decrypt(symmetricKeyCiphertext: encrypted.first!, payloadCiphertext: encrypted.second!, privateKey: privateKey) 25 | let str = String(decoding: decrypted!, as: UTF8.self) 26 | 27 | if let str = NSString(data: decrypted!, encoding: NSUTF8StringEncoding) as? String { 28 | print("decrypted->>") 29 | print(str) 30 | } else { 31 | print("not a valid UTF-8 sequence") 32 | } 33 | } 34 | 35 | var body: some View { 36 | VStack { 37 | Image(systemName: "globe") 38 | .imageScale(.large) 39 | .foregroundColor(.accentColor) 40 | } 41 | .padding() 42 | } 43 | } 44 | 45 | struct ContentView_Previews: PreviewProvider { 46 | static var previews: some View { 47 | ContentView() 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /sampleappUITests/sampleappUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // sampleappUITests.swift 3 | // sampleappUITests 4 | // 5 | // Created by Anmol Verma on 02/12/22. 6 | // 7 | 8 | import XCTest 9 | 10 | final class sampleappUITests: XCTestCase { 11 | 12 | override func setUpWithError() throws { 13 | // Put setup code here. This method is called before the invocation of each test method in the class. 14 | 15 | // In UI tests it is usually best to stop immediately when a failure occurs. 16 | continueAfterFailure = false 17 | 18 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 19 | } 20 | 21 | override func tearDownWithError() throws { 22 | // Put teardown code here. This method is called after the invocation of each test method in the class. 23 | } 24 | 25 | func testExample() throws { 26 | // UI tests must launch the application that they test. 27 | let app = XCUIApplication() 28 | app.launch() 29 | 30 | // Use XCTAssert and related functions to verify your tests produce the correct results. 31 | } 32 | 33 | func testLaunchPerformance() throws { 34 | if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 7.0, *) { 35 | // This measures how long it takes to launch your application. 36 | measure(metrics: [XCTApplicationLaunchMetric()]) { 37 | XCUIApplication().launch() 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /capillaryslackTests/capillaryslackTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // capillaryslackTests.swift 3 | // capillaryslackTests 4 | // 5 | // Created by Anmol Verma on 23/11/22. 6 | // 7 | 8 | import XCTest 9 | @testable import capillaryslack 10 | 11 | final class capillaryslackTests: XCTestCase { 12 | override func setUpWithError() throws { 13 | // Put setup code here. This method is called before the invocation of each test method in the class. 14 | } 15 | 16 | override func tearDownWithError() throws { 17 | // Put teardown code here. This method is called after the invocation of each test method in the class. 18 | } 19 | 20 | func testEncryptAndDecryptWithRSA() throws { 21 | CapillaryIOS.initNow(chainId: "anmol",isUnitTest: true) 22 | 23 | let publicKey = CapillaryIOS.publicKey(chainId: "anmol") 24 | let privateKey = CapillaryIOS.privateKey(chainId: "anmol")! 25 | 26 | let encrypted = CapillaryIOS.encrypt(data: "anmol".data(using: .utf8)!, publicKey: publicKey!) 27 | XCTAssertNotNil(encrypted) 28 | 29 | let decrypted = CapillaryIOS.decrypt(symmetricKeyCiphertext: encrypted.first!, payloadCiphertext: encrypted.second!, privateKey: privateKey) 30 | XCTAssertNotNil(decrypted) 31 | 32 | let str = String(decoding: decrypted!, as: UTF8.self) 33 | XCTAssertEqual(str, "anmol") 34 | } 35 | 36 | func testPerformanceExample() throws { 37 | // This is an example of a performance test case. 38 | self.measure { 39 | // Put the code you want to measure the time of here. 40 | } 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /capillaryslack/Signature.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Signature.swift 3 | // SwiftyRSA 4 | // 5 | // Created by Loïs Di Qual on 9/19/16. 6 | // Copyright © 2016 Scoop. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public class Signature { 12 | 13 | public enum DigestType { 14 | case sha1 15 | case sha224 16 | case sha256 17 | case sha384 18 | case sha512 19 | 20 | var padding: Padding { 21 | switch self { 22 | case .sha1: return .PKCS1SHA1 23 | case .sha224: return .PKCS1SHA224 24 | case .sha256: return .PKCS1SHA256 25 | case .sha384: return .PKCS1SHA384 26 | case .sha512: return .PKCS1SHA512 27 | } 28 | } 29 | } 30 | 31 | /// Data of the signature 32 | public let data: Data 33 | 34 | /// Creates a signature with data. 35 | /// 36 | /// - Parameter data: Data of the signature 37 | public init(data: Data) { 38 | self.data = data 39 | } 40 | 41 | /// Creates a signature with a base64-encoded string. 42 | /// 43 | /// - Parameter base64String: Base64-encoded representation of the signature data. 44 | /// - Throws: SwiftyRSAError 45 | public convenience init(base64Encoded base64String: String) throws { 46 | guard let data = Data(base64Encoded: base64String) else { 47 | throw SwiftyRSAError.invalidBase64String 48 | } 49 | self.init(data: data) 50 | } 51 | 52 | /// Returns the base64 representation of the signature. 53 | public var base64String: String { 54 | return data.base64EncodedString() 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /capillaryslack/Data+SHA.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Data+SHA.swift 3 | // 4 | // 5 | // Created by Joanna Bednarz on 02/10/2020. 6 | // 7 | 8 | import Foundation 9 | import CommonCrypto 10 | 11 | extension Data { 12 | 13 | func swiftyRSASHA1() -> Data { 14 | var digest = [UInt8](repeating: 0, count: Int(CC_SHA1_DIGEST_LENGTH)) 15 | withUnsafeBytes { (buffer: UnsafeRawBufferPointer) in 16 | _ = CC_SHA1(buffer.baseAddress, CC_LONG(count), &digest) 17 | } 18 | return Data(digest) 19 | } 20 | 21 | func swiftyRSASHA224() -> Data { 22 | var digest = [UInt8](repeating: 0, count: Int(CC_SHA224_DIGEST_LENGTH)) 23 | withUnsafeBytes { (buffer: UnsafeRawBufferPointer) in 24 | _ = CC_SHA224(buffer.baseAddress, CC_LONG(count), &digest) 25 | } 26 | return Data(digest) 27 | } 28 | 29 | func swiftyRSASHA256() -> Data { 30 | var digest = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH)) 31 | withUnsafeBytes { (buffer: UnsafeRawBufferPointer) in 32 | _ = CC_SHA256(buffer.baseAddress, CC_LONG(count), &digest) 33 | } 34 | return Data(digest) 35 | } 36 | 37 | func swiftyRSASHA384() -> Data { 38 | var digest = [UInt8](repeating: 0, count: Int(CC_SHA384_DIGEST_LENGTH)) 39 | withUnsafeBytes { (buffer: UnsafeRawBufferPointer) in 40 | _ = CC_SHA384(buffer.baseAddress, CC_LONG(count), &digest) 41 | } 42 | return Data(digest) 43 | } 44 | 45 | func swiftyRSASHA512() -> Data { 46 | var digest = [UInt8](repeating: 0, count: Int(CC_SHA512_DIGEST_LENGTH)) 47 | withUnsafeBytes { (buffer: UnsafeRawBufferPointer) in 48 | _ = CC_SHA512(buffer.baseAddress, CC_LONG(count), &digest) 49 | } 50 | return Data(digest) 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /capillaryslack/PrivateKey.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PrivateKey.swift 3 | // SwiftyRSA 4 | // 5 | // Created by Lois Di Qual on 5/17/17. 6 | // Copyright © 2017 Scoop. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public class PrivateKey: Key { 12 | 13 | /// Reference to the key within the keychain 14 | public let reference: SecKey 15 | 16 | /// Original data of the private key. 17 | /// Note that it does not contain PEM headers and holds data as bytes, not as a base 64 string. 18 | public let originalData: Data? 19 | 20 | let tag: String? 21 | 22 | /// Returns a PEM representation of the private key. 23 | /// 24 | /// - Returns: Data of the key, PEM-encoded 25 | /// - Throws: SwiftyRSAError 26 | public func pemString() throws -> String { 27 | let data = try self.data() 28 | let pem = SwiftyRSA.format(keyData: data, withPemType: "RSA PRIVATE KEY") 29 | return pem 30 | } 31 | 32 | /// Creates a private key with a keychain key reference. 33 | /// This initializer will throw if the provided key reference is not a private RSA key. 34 | /// 35 | /// - Parameter reference: Reference to the key within the keychain. 36 | /// - Throws: SwiftyRSAError 37 | public required init(reference: SecKey) throws { 38 | 39 | guard SwiftyRSA.isValidKeyReference(reference, forClass: kSecAttrKeyClassPrivate) else { 40 | throw SwiftyRSAError.notAPrivateKey 41 | } 42 | 43 | self.reference = reference 44 | self.tag = nil 45 | self.originalData = nil 46 | } 47 | 48 | /// Creates a private key with a RSA public key data. 49 | /// 50 | /// - Parameter data: Private key data 51 | /// - Throws: SwiftyRSAError 52 | required public init(data: Data) throws { 53 | self.originalData = data 54 | let tag = UUID().uuidString 55 | self.tag = tag 56 | let dataWithoutHeader = try SwiftyRSA.stripKeyHeader(keyData: data) 57 | reference = try SwiftyRSA.addKey(dataWithoutHeader, isPublic: false, tag: tag) 58 | } 59 | 60 | deinit { 61 | if let tag = tag { 62 | SwiftyRSA.removeKey(tag: tag) 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /capillaryslack/EncryptedMessage.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EncryptedMessage.swift 3 | // SwiftyRSA 4 | // 5 | // Created by Lois Di Qual on 5/18/17. 6 | // Copyright © 2017 Scoop. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public class EncryptedMessage: Message { 12 | 13 | /// Data of the message 14 | public let data: Data 15 | 16 | /// Creates an encrypted message with data. 17 | /// 18 | /// - Parameter data: Data of the encrypted message. 19 | public required init(data: Data) { 20 | self.data = data 21 | } 22 | 23 | /// Decrypts an encrypted message with a private key and returns a clear message. 24 | /// 25 | /// - Parameters: 26 | /// - key: Private key to decrypt the mssage with 27 | /// - padding: Padding to use during the decryption 28 | /// - Returns: Clear message 29 | /// - Throws: SwiftyRSAError 30 | public func decrypted(with key: PrivateKey, padding: Padding) throws -> ClearMessage { 31 | let blockSize = SecKeyGetBlockSize(key.reference) 32 | 33 | var encryptedDataAsArray = [UInt8](repeating: 0, count: data.count) 34 | (data as NSData).getBytes(&encryptedDataAsArray, length: data.count) 35 | 36 | var decryptedDataBytes = [UInt8](repeating: 0, count: 0) 37 | var idx = 0 38 | while idx < encryptedDataAsArray.count { 39 | 40 | let idxEnd = min(idx + blockSize, encryptedDataAsArray.count) 41 | let chunkData = [UInt8](encryptedDataAsArray[idx.. String 25 | func data() throws -> Data 26 | func base64String() throws -> String 27 | } 28 | 29 | public extension Key { 30 | 31 | /// Returns a Base64 representation of the public key. 32 | /// 33 | /// - Returns: Data of the key, Base64-encoded 34 | /// - Throws: SwiftyRSAError 35 | func base64String() throws -> String { 36 | return try data().base64EncodedString() 37 | } 38 | 39 | func data() throws -> Data { 40 | return try SwiftyRSA.data(forKeyReference: reference) 41 | } 42 | 43 | /// Creates a public key with a base64-encoded string. 44 | /// 45 | /// - Parameter base64String: Base64-encoded public key data 46 | /// - Throws: SwiftyRSAError 47 | init(base64Encoded base64String: String) throws { 48 | guard let data = Data(base64Encoded: base64String, options: [.ignoreUnknownCharacters]) else { 49 | throw SwiftyRSAError.invalidBase64String 50 | } 51 | try self.init(data: data) 52 | } 53 | 54 | /// Creates a public key with a PEM string. 55 | /// 56 | /// - Parameter pemString: PEM-encoded public key string 57 | /// - Throws: SwiftyRSAError 58 | init(pemEncoded pemString: String) throws { 59 | let base64String = try SwiftyRSA.base64String(pemEncoded: pemString) 60 | try self.init(base64Encoded: base64String) 61 | } 62 | 63 | /// Creates a public key with a PEM file. 64 | /// 65 | /// - Parameters: 66 | /// - pemName: Name of the PEM file 67 | /// - bundle: Bundle in which to look for the PEM file. Defaults to the main bundle. 68 | /// - Throws: SwiftyRSAError 69 | init(pemNamed pemName: String, in bundle: Bundle = Bundle.main) throws { 70 | guard let path = bundle.path(forResource: pemName, ofType: "pem") else { 71 | throw SwiftyRSAError.pemFileNotFound(name: pemName) 72 | } 73 | let keyString = try String(contentsOf: URL(fileURLWithPath: path), encoding: .utf8) 74 | try self.init(pemEncoded: keyString) 75 | } 76 | 77 | /// Creates a private key with a DER file. 78 | /// 79 | /// - Parameters: 80 | /// - derName: Name of the DER file 81 | /// - bundle: Bundle in which to look for the DER file. Defaults to the main bundle. 82 | /// - Throws: SwiftyRSAError 83 | init(derNamed derName: String, in bundle: Bundle = Bundle.main) throws { 84 | guard let path = bundle.path(forResource: derName, ofType: "der") else { 85 | throw SwiftyRSAError.derFileNotFound(name: derName) 86 | } 87 | let data = try Data(contentsOf: URL(fileURLWithPath: path)) 88 | try self.init(data: data) 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /capillaryslack/capillaryios.swift: -------------------------------------------------------------------------------- 1 | // 2 | // capillaryios.swift 3 | // capillaryios 4 | // 5 | // Created by Anmol Verma on 15/11/22. 6 | // 7 | 8 | import Foundation 9 | import CryptoKit 10 | 11 | @objc public class CapillaryIOS: NSObject { 12 | 13 | @objc public class func initNow(chainId : String, isUnitTest:Bool) { 14 | let keys = try! SwiftyRSA.generateRSAKeyPair(sizeInBits: 2048, applyUnitTestWorkaround: isUnitTest, chainId: chainId) 15 | let result = try! SwiftyRSA.addKey(keys.privateKey.data(), isPublic: false, tag: "\(chainId).private") 16 | let resultR = try! SwiftyRSA.addKey(keys.publicKey.data(), isPublic: true, tag: "\(chainId).public") 17 | } 18 | 19 | @objc public class func publicKey(chainId : String) -> Data? { 20 | let publicKey = SwiftyRSA.getKeyTypeInKeyChain(tag: "\(chainId).public".data(using: .utf8)!,keyClass: kSecAttrKeyClassPublic as String) 21 | return try! SwiftyRSA.prependX509KeyHeader(keyData: PublicKey(reference: publicKey!).data()) 22 | } 23 | 24 | @objc public class func privateKey(chainId : String) -> Data? { 25 | let privateKey = SwiftyRSA.getKeyTypeInKeyChain(tag: "\(chainId).private".data(using: .utf8)!,keyClass: kSecAttrKeyClassPrivate as String) 26 | return try! PrivateKey(reference: privateKey!).data() 27 | } 28 | 29 | @objc public class func encrypt(data:Data,publicKey:Data) -> EncryptedData { 30 | // we create a symmetric key 31 | let symmetricKeyBytes = SymmetricKey(size: .bits256) 32 | let cryptedBox = try! ChaChaPoly.seal(data, using: symmetricKeyBytes) 33 | let sealedBox = try! ChaChaPoly.SealedBox(combined: cryptedBox.combined) 34 | let payloadCiphertext = sealedBox.combined 35 | //we encrypt the data with symmtric key 36 | 37 | let symmetricKeyCiphertext = try! StoredKey(PublicKey(data: publicKey).reference).encryptBytes(symmetricKeyBytes.rawRepresentation).message 38 | // we encrypte the symmetric key 39 | return EncryptedData(first: symmetricKeyCiphertext.base64EncodedString(options: []) , second: payloadCiphertext.base64EncodedString(options: []) ) 40 | } 41 | 42 | @objc public class func decrypt( 43 | symmetricKeyCiphertext:String, 44 | payloadCiphertext:String, 45 | privateKey:Data 46 | ) -> Data? { 47 | let decryptedSymmetricKeyBytes = try! StoredKey(PrivateKey(data: privateKey).reference).decryptAsData(Data.fromBase64(symmetricKeyCiphertext)!) 48 | let symmetricKeyBytes = try! SymmetricKey(rawRepresentation: decryptedSymmetricKeyBytes) 49 | 50 | let sealedBoxToOpen = try! ChaChaPoly.SealedBox(combined: Data.fromBase64(payloadCiphertext)!) 51 | let decryptedData = try! ChaChaPoly.open(sealedBoxToOpen, using: symmetricKeyBytes) 52 | return decryptedData 53 | } 54 | 55 | @objc public class func publicKeyFromBytes(data:Data) -> Data? { 56 | return try! PublicKey(data: data).data() 57 | } 58 | 59 | @objc public class func privateKeyFromBytes(data:Data) -> Data? { 60 | return try! PrivateKey(data: data).data() 61 | } 62 | 63 | } 64 | 65 | extension String { 66 | public static func fromBase64(_ encoded: String) -> String? { 67 | if let data = Data.fromBase64(encoded) { 68 | return String(data: data, encoding: .utf8) 69 | } 70 | return nil; 71 | } 72 | } 73 | 74 | extension Data { 75 | /// Same as ``Data(base64Encoded:)``, but adds padding automatically 76 | /// (if missing, instead of returning `nil`). 77 | public static func fromBase64(_ encoded: String) -> Data? { 78 | // Prefixes padding-character(s) (if needed). 79 | var encoded = encoded; 80 | let remainder = encoded.count % 4 81 | if remainder > 0 { 82 | encoded = encoded.padding( 83 | toLength: encoded.count + 4 - remainder, 84 | withPad: "=", startingAt: 0); 85 | } 86 | 87 | // Finally, decode. 88 | return Data(base64Encoded: encoded); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /capillaryslack/SwiftyRSAError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftyRSAError.swift 3 | // SwiftyRSA 4 | // 5 | // Created by Lois Di Qual on 5/15/17. 6 | // Contributions by Stchepinsky Nathan on 24/06/2021 7 | // Copyright © 2017 Scoop. All rights reserved. 8 | // 9 | 10 | import Foundation 11 | 12 | public enum SwiftyRSAError: Error { 13 | 14 | case pemDoesNotContainKey 15 | case keyRepresentationFailed(error: CFError?) 16 | case keyGenerationFailed(error: CFError?) 17 | case keyCreateFailed(error: CFError?) 18 | case keyAddFailed(status: OSStatus) 19 | case keyCopyFailed(status: OSStatus) 20 | case tagEncodingFailed 21 | case asn1ParsingFailed 22 | case invalidAsn1RootNode 23 | case invalidAsn1Structure 24 | case invalidBase64String 25 | case chunkDecryptFailed(index: Int) 26 | case chunkEncryptFailed(index: Int) 27 | case stringToDataConversionFailed 28 | case dataToStringConversionFailed 29 | case invalidDigestSize(digestSize: Int, maxChunkSize: Int) 30 | case signatureCreateFailed(status: OSStatus) 31 | case signatureVerifyFailed(status: OSStatus) 32 | case pemFileNotFound(name: String) 33 | case derFileNotFound(name: String) 34 | case notAPublicKey 35 | case notAPrivateKey 36 | case x509CertificateFailed 37 | 38 | var localizedDescription: String { 39 | switch self { 40 | case .pemDoesNotContainKey: 41 | return "Couldn't get data from PEM key: no data available after stripping headers" 42 | case .keyRepresentationFailed(let error): 43 | return "Couldn't retrieve key data from the keychain: CFError \(String(describing: error))" 44 | case .keyGenerationFailed(let error): 45 | return "Couldn't generate key pair: CFError: \(String(describing: error))" 46 | case .keyCreateFailed(let error): 47 | return "Couldn't create key reference from key data: CFError \(String(describing: error))" 48 | case .keyAddFailed(let status): 49 | return "Couldn't retrieve key data from the keychain: OSStatus \(status)" 50 | case .keyCopyFailed(let status): 51 | return "Couldn't copy and retrieve key reference from the keychain: OSStatus \(status)" 52 | case .tagEncodingFailed: 53 | return "Couldn't create tag data for key" 54 | case .asn1ParsingFailed: 55 | return "Couldn't parse the ASN1 key data. Please file a bug at https://goo.gl/y67MW6" 56 | case .invalidAsn1RootNode: 57 | return "Couldn't parse the provided key because its root ASN1 node is not a sequence. The key is probably corrupt" 58 | case .invalidAsn1Structure: 59 | return "Couldn't parse the provided key because it has an unexpected ASN1 structure" 60 | case .invalidBase64String: 61 | return "The provided string is not a valid Base 64 string" 62 | case .chunkDecryptFailed(let index): 63 | return "Couldn't decrypt chunk at index \(index)" 64 | case .chunkEncryptFailed(let index): 65 | return "Couldn't encrypt chunk at index \(index)" 66 | case .stringToDataConversionFailed: 67 | return "Couldn't convert string to data using specified encoding" 68 | case .dataToStringConversionFailed: 69 | return "Couldn't convert data to string representation" 70 | case .invalidDigestSize(let digestSize, let maxChunkSize): 71 | return "Provided digest type produces a size (\(digestSize)) that is bigger than the maximum chunk size \(maxChunkSize) of the RSA key" 72 | case .signatureCreateFailed(let status): 73 | return "Couldn't sign provided data: OSStatus \(status)" 74 | case .signatureVerifyFailed(let status): 75 | return "Couldn't verify signature of the provided data: OSStatus \(status)" 76 | case .pemFileNotFound(let name): 77 | return "Couldn't find a PEM file named '\(name)'" 78 | case .derFileNotFound(let name): 79 | return "Couldn't find a DER file named '\(name)'" 80 | case .notAPublicKey: 81 | return "Provided key is not a valid RSA public key" 82 | case .notAPrivateKey: 83 | return "Provided key is not a valid RSA pivate key" 84 | case .x509CertificateFailed : 85 | return "Couldn't prepend the provided key because it has an unexpected structure" 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /capillaryslack/PublicKey.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PublicKey.swift 3 | // SwiftyRSA 4 | // 5 | // Created by Lois Di Qual on 5/17/17. 6 | // Copyright © 2017 Scoop. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public class PublicKey: Key { 12 | 13 | /// Reference to the key within the keychain 14 | public let reference: SecKey 15 | 16 | /// Data of the public key as provided when creating the key. 17 | /// Note that if the key was created from a base64string / DER string / PEM file / DER file, 18 | /// the data holds the actual bytes of the key, not any textual representation like PEM headers 19 | /// or base64 characters. 20 | public let originalData: Data? 21 | 22 | let tag: String? // Only used on iOS 8/9 23 | 24 | /// Returns a PEM representation of the public key. 25 | /// 26 | /// - Returns: Data of the key, PEM-encoded 27 | /// - Throws: SwiftyRSAError 28 | public func pemString() throws -> String { 29 | let data = try self.data() 30 | let pem = SwiftyRSA.format(keyData: data, withPemType: "RSA PUBLIC KEY") 31 | return pem 32 | } 33 | 34 | /// Creates a public key with a keychain key reference. 35 | /// This initializer will throw if the provided key reference is not a public RSA key. 36 | /// 37 | /// - Parameter reference: Reference to the key within the keychain. 38 | /// - Throws: SwiftyRSAError 39 | public required init(reference: SecKey) throws { 40 | 41 | guard SwiftyRSA.isValidKeyReference(reference, forClass: kSecAttrKeyClassPublic) else { 42 | throw SwiftyRSAError.notAPublicKey 43 | } 44 | 45 | self.reference = reference 46 | self.tag = nil 47 | self.originalData = nil 48 | } 49 | 50 | /// Data of the public key as returned by the keychain. 51 | /// This method throws if SwiftyRSA cannot extract data from the key. 52 | /// 53 | /// - Returns: Data of the public key as returned by the keychain. 54 | /// - Throws: SwiftyRSAError 55 | required public init(data: Data) throws { 56 | 57 | let tag = UUID().uuidString 58 | self.tag = tag 59 | 60 | self.originalData = data 61 | let dataWithoutHeader = try SwiftyRSA.stripKeyHeader(keyData: data) 62 | 63 | reference = try SwiftyRSA.addKey(dataWithoutHeader, isPublic: true, tag: tag) 64 | } 65 | 66 | static let publicKeyRegex: NSRegularExpression? = { 67 | let publicKeyRegex = "(-----BEGIN PUBLIC KEY-----.+?-----END PUBLIC KEY-----)" 68 | return try? NSRegularExpression(pattern: publicKeyRegex, options: .dotMatchesLineSeparators) 69 | }() 70 | 71 | /// Takes an input string, scans for public key sections, and then returns a PublicKey for any valid keys found 72 | /// - This method scans the file for public key armor - if no keys are found, an empty array is returned 73 | /// - Each public key block found is "parsed" by `publicKeyFromPEMString()` 74 | /// - should that method throw, the error is _swallowed_ and not rethrown 75 | /// 76 | /// - parameter pemString: The string to use to parse out values 77 | /// 78 | /// - returns: An array of `PublicKey` objects 79 | public static func publicKeys(pemEncoded pemString: String) -> [PublicKey] { 80 | 81 | // If our regexp isn't valid, or the input string is empty, we can't move forward… 82 | guard let publicKeyRegexp = publicKeyRegex, pemString.count > 0 else { 83 | return [] 84 | } 85 | 86 | let all = NSRange( 87 | location: 0, 88 | length: pemString.count 89 | ) 90 | 91 | let matches = publicKeyRegexp.matches( 92 | in: pemString, 93 | options: NSRegularExpression.MatchingOptions(rawValue: 0), 94 | range: all 95 | ) 96 | 97 | let keys = matches.compactMap { result -> PublicKey? in 98 | 99 | let match = result.range(at: 1) 100 | let start = pemString.index(pemString.startIndex, offsetBy: match.location) 101 | let end = pemString.index(start, offsetBy: match.length) 102 | 103 | let thisKey = pemString[start.. [CUnsignedChar] { 14 | // Short form 15 | if self < 128 { 16 | return [CUnsignedChar(self)] 17 | } 18 | 19 | // Long form 20 | let long = Int(log2(Double(self)) / 8 + 1) 21 | var len = self 22 | var result: [CUnsignedChar] = [CUnsignedChar(long + 0x80)] 23 | 24 | for _ in 0..> 8 27 | } 28 | 29 | return result 30 | } 31 | 32 | init?(octetBytes: [CUnsignedChar], startIdx: inout NSInteger) { 33 | if octetBytes[startIdx] < 128 { 34 | // Short form 35 | self.init(octetBytes[startIdx]) 36 | startIdx += 1 37 | } else { 38 | // Long form 39 | let octets = NSInteger(octetBytes[startIdx] as UInt8 - 128) 40 | 41 | if octets > octetBytes.count - startIdx { 42 | self.init(0) 43 | return nil 44 | } 45 | 46 | var result = UInt64(0) 47 | 48 | for octet in 1...octets { 49 | result = (result << 8) 50 | result = result + UInt64(octetBytes[startIdx + octet]) 51 | } 52 | 53 | startIdx += 1 + octets 54 | self.init(result) 55 | } 56 | } 57 | } 58 | 59 | public extension Data { 60 | // This code source come from Heimdall project https://github.com/henrinormak/Heimdall published under MIT Licence 61 | 62 | /// This method prepend the X509 header to a given public key 63 | func prependx509Header() -> Data { 64 | let result = NSMutableData() 65 | 66 | let encodingLength: Int = (self.count + 1).encodedOctets().count 67 | let OID: [CUnsignedChar] = [0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 68 | 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00] 69 | 70 | var builder: [CUnsignedChar] = [] 71 | 72 | // ASN.1 SEQUENCE 73 | builder.append(0x30) 74 | 75 | // Overall size, made of OID + bitstring encoding + actual key 76 | let size = OID.count + 2 + encodingLength + self.count 77 | let encodedSize = size.encodedOctets() 78 | builder.append(contentsOf: encodedSize) 79 | result.append(builder, length: builder.count) 80 | result.append(OID, length: OID.count) 81 | builder.removeAll(keepingCapacity: false) 82 | 83 | builder.append(0x03) 84 | builder.append(contentsOf: (self.count + 1).encodedOctets()) 85 | builder.append(0x00) 86 | result.append(builder, length: builder.count) 87 | 88 | // Actual key bytes 89 | result.append(self) 90 | 91 | return result as Data 92 | } 93 | 94 | func hasX509Header() throws -> Bool { 95 | let node: Asn1Parser.Node 96 | do { 97 | node = try Asn1Parser.parse(data: self) 98 | } catch { 99 | throw SwiftyRSAError.asn1ParsingFailed 100 | } 101 | 102 | // Ensure the raw data is an ASN1 sequence 103 | guard case .sequence(let nodes) = node else { 104 | return false 105 | } 106 | 107 | // Must contain 2 elements, a sequence and a bit string 108 | if nodes.count != 2 { 109 | return false 110 | } 111 | 112 | // Ensure the first node is an ASN1 sequence 113 | guard case .sequence(let firstNode) = nodes[0] else { 114 | return false 115 | } 116 | 117 | // Must contain 2 elements, an object id and NULL 118 | if firstNode.count != 2 { 119 | return false 120 | } 121 | 122 | guard case .objectIdentifier(_) = firstNode[0] else { 123 | return false 124 | } 125 | 126 | guard case .null = firstNode[1] else { 127 | return false 128 | } 129 | 130 | // The 2sd child has to be a bit string containing a sequence of 2 int 131 | let last = nodes[1] 132 | if case .bitString(let secondChildSequence) = last { 133 | return try secondChildSequence.isAnHeaderlessKey() 134 | } else { 135 | return false 136 | } 137 | } 138 | 139 | func isAnHeaderlessKey() throws -> Bool { 140 | let node: Asn1Parser.Node 141 | do { 142 | node = try Asn1Parser.parse(data: self) 143 | } catch { 144 | throw SwiftyRSAError.asn1ParsingFailed 145 | } 146 | 147 | // Ensure the raw data is an ASN1 sequence 148 | guard case .sequence(let nodes) = node else { 149 | return false 150 | } 151 | 152 | // Detect whether the sequence only has integers, in which case it's a headerless key 153 | let onlyHasIntegers = nodes.filter { node -> Bool in 154 | if case .integer = node { 155 | return false 156 | } 157 | return true 158 | }.isEmpty 159 | 160 | // Headerless key 161 | return onlyHasIntegers 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /capillaryslack/ClearMessage.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ClearMessage.swift 3 | // SwiftyRSA 4 | // 5 | // Created by Lois Di Qual on 5/18/17. 6 | // Copyright © 2017 Scoop. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public class ClearMessage: Message { 12 | 13 | /// Data of the message 14 | public let data: Data 15 | 16 | /// Creates a clear message with data. 17 | /// 18 | /// - Parameter data: Data of the clear message 19 | public required init(data: Data) { 20 | self.data = data 21 | } 22 | 23 | /// Creates a clear message from a string, with the specified encoding. 24 | /// 25 | /// - Parameters: 26 | /// - string: String value of the clear message 27 | /// - encoding: Encoding to use to generate the clear data 28 | /// - Throws: SwiftyRSAError 29 | public convenience init(string: String, using encoding: String.Encoding) throws { 30 | guard let data = string.data(using: encoding) else { 31 | throw SwiftyRSAError.stringToDataConversionFailed 32 | } 33 | self.init(data: data) 34 | } 35 | 36 | /// Returns the string representation of the clear message using the specified 37 | /// string encoding. 38 | /// 39 | /// - Parameter encoding: Encoding to use during the string conversion 40 | /// - Returns: String representation of the clear message 41 | /// - Throws: SwiftyRSAError 42 | public func string(encoding: String.Encoding) throws -> String { 43 | guard let str = String(data: data, encoding: encoding) else { 44 | throw SwiftyRSAError.dataToStringConversionFailed 45 | } 46 | return str 47 | } 48 | 49 | /// Encrypts a clear message with a public key and returns an encrypted message. 50 | /// 51 | /// - Parameters: 52 | /// - key: Public key to encrypt the clear message with 53 | /// - padding: Padding to use during the encryption 54 | /// - Returns: Encrypted message 55 | /// - Throws: SwiftyRSAError 56 | public func encrypted(with key: PublicKey, padding: Padding) throws -> EncryptedMessage { 57 | 58 | let blockSize = SecKeyGetBlockSize(key.reference) 59 | 60 | var maxChunkSize: Int 61 | switch padding { 62 | case []: 63 | maxChunkSize = blockSize 64 | case .OAEP: 65 | maxChunkSize = blockSize - 42 66 | default: 67 | maxChunkSize = blockSize - 11 68 | } 69 | 70 | var decryptedDataAsArray = [UInt8](repeating: 0, count: data.count) 71 | (data as NSData).getBytes(&decryptedDataAsArray, length: data.count) 72 | 73 | var encryptedDataBytes = [UInt8](repeating: 0, count: 0) 74 | var idx = 0 75 | while idx < decryptedDataAsArray.count { 76 | 77 | let idxEnd = min(idx + maxChunkSize, decryptedDataAsArray.count) 78 | let chunkData = [UInt8](decryptedDataAsArray[idx.. Signature { 108 | 109 | let digest = self.digest(digestType: digestType) 110 | let blockSize = SecKeyGetBlockSize(key.reference) 111 | let maxChunkSize = blockSize - 11 112 | 113 | guard digest.count <= maxChunkSize else { 114 | throw SwiftyRSAError.invalidDigestSize(digestSize: digest.count, maxChunkSize: maxChunkSize) 115 | } 116 | 117 | var digestBytes = [UInt8](repeating: 0, count: digest.count) 118 | (digest as NSData).getBytes(&digestBytes, length: digest.count) 119 | 120 | var signatureBytes = [UInt8](repeating: 0, count: blockSize) 121 | var signatureDataLength = blockSize 122 | 123 | let status = SecKeyRawSign(key.reference, digestType.padding, digestBytes, digestBytes.count, &signatureBytes, &signatureDataLength) 124 | 125 | guard status == noErr else { 126 | throw SwiftyRSAError.signatureCreateFailed(status: status) 127 | } 128 | 129 | let signatureData = Data(bytes: signatureBytes, count: signatureBytes.count) 130 | return Signature(data: signatureData) 131 | } 132 | 133 | /// Verifies the signature of a clear message. 134 | /// 135 | /// - Parameters: 136 | /// - key: Public key to verify the signature with 137 | /// - signature: Signature to verify 138 | /// - digestType: Digest type used for the signature 139 | /// - Returns: Result of the verification 140 | /// - Throws: SwiftyRSAError 141 | public func verify(with key: PublicKey, signature: Signature, digestType: Signature.DigestType) throws -> Bool { 142 | 143 | let digest = self.digest(digestType: digestType) 144 | var digestBytes = [UInt8](repeating: 0, count: digest.count) 145 | (digest as NSData).getBytes(&digestBytes, length: digest.count) 146 | 147 | var signatureBytes = [UInt8](repeating: 0, count: signature.data.count) 148 | (signature.data as NSData).getBytes(&signatureBytes, length: signature.data.count) 149 | 150 | let status = SecKeyRawVerify(key.reference, digestType.padding, digestBytes, digestBytes.count, signatureBytes, signatureBytes.count) 151 | 152 | if status == errSecSuccess { 153 | return true 154 | } else if status == -9809 { 155 | return false 156 | } else { 157 | throw SwiftyRSAError.signatureVerifyFailed(status: status) 158 | } 159 | } 160 | 161 | func digest(digestType: Signature.DigestType) -> Data { 162 | 163 | let digest: Data 164 | 165 | switch digestType { 166 | case .sha1: 167 | digest = data.swiftyRSASHA1() 168 | case .sha224: 169 | digest = data.swiftyRSASHA224() 170 | case .sha256: 171 | digest = data.swiftyRSASHA256() 172 | case .sha384: 173 | digest = data.swiftyRSASHA384() 174 | case .sha512: 175 | digest = data.swiftyRSASHA512() 176 | } 177 | 178 | return digest 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /capillaryslack/Asn1Parser.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Asn1Parser.swift 3 | // SwiftyRSA 4 | // 5 | // Created by Lois Di Qual on 5/9/17. 6 | // Copyright © 2017 Scoop. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /// Simple data scanner that consumes bytes from a raw data and keeps an updated position. 12 | private class Scanner { 13 | 14 | enum ScannerError: Error { 15 | case outOfBounds 16 | } 17 | 18 | let data: Data 19 | var index: Int = 0 20 | 21 | /// Returns whether there is no more data to consume 22 | var isComplete: Bool { 23 | return index >= data.count 24 | } 25 | 26 | /// Creates a scanner with provided data 27 | /// 28 | /// - Parameter data: Data to consume 29 | init(data: Data) { 30 | self.data = data 31 | } 32 | 33 | /// Consumes data of provided length and returns it 34 | /// 35 | /// - Parameter length: length of the data to consume 36 | /// - Returns: data consumed 37 | /// - Throws: ScannerError.outOfBounds error if asked to consume too many bytes 38 | func consume(length: Int) throws -> Data { 39 | 40 | guard length > 0 else { 41 | return Data() 42 | } 43 | 44 | guard index + length <= data.count else { 45 | throw ScannerError.outOfBounds 46 | } 47 | 48 | let subdata = data.subdata(in: index.. Int { 65 | 66 | let lengthByte = try consume(length: 1).firstByte 67 | 68 | // If the first byte's value is less than 0x80, it directly contains the length 69 | // so we can return it 70 | guard lengthByte >= 0x80 else { 71 | return Int(lengthByte) 72 | } 73 | 74 | // If the first byte's value is more than 0x80, it indicates how many following bytes 75 | // will describe the length. For instance, 0x85 indicates that 0x85 - 0x80 = 0x05 = 5 76 | // bytes will describe the length, so we need to read the 5 next bytes and get their integer 77 | // value to determine the length. 78 | let nextByteCount = lengthByte - 0x80 79 | let length = try consume(length: Int(nextByteCount)) 80 | 81 | return length.integer 82 | } 83 | } 84 | 85 | private extension Data { 86 | 87 | /// Returns the first byte of the current data 88 | var firstByte: UInt8 { 89 | var byte: UInt8 = 0 90 | copyBytes(to: &byte, count: MemoryLayout.size) 91 | return byte 92 | } 93 | 94 | /// Returns the integer value of the current data. 95 | /// @warning: this only supports data up to 4 bytes, as we can only extract 32-bit integers. 96 | var integer: Int { 97 | 98 | guard count > 0 else { 99 | return 0 100 | } 101 | 102 | var int: UInt32 = 0 103 | var offset: Int32 = Int32(count - 1) 104 | forEach { byte in 105 | let byte32 = UInt32(byte) 106 | let shifted = byte32 << (UInt32(offset) * 8) 107 | int = int | shifted 108 | offset -= 1 109 | } 110 | 111 | return Int(int) 112 | } 113 | } 114 | 115 | /// A simple ASN1 parser that will recursively iterate over a root node and return a Node tree. 116 | /// The root node can be any of the supported nodes described in `Node`. If the parser encounters a sequence 117 | /// it will recursively parse its children. 118 | enum Asn1Parser { 119 | 120 | /// An ASN1 node 121 | enum Node { 122 | case sequence(nodes: [Node]) 123 | case integer(data: Data) 124 | case objectIdentifier(data: Data) 125 | case null 126 | case bitString(data: Data) 127 | case octetString(data: Data) 128 | } 129 | 130 | enum ParserError: Error { 131 | case noType 132 | case invalidType(value: UInt8) 133 | } 134 | 135 | /// Parses ASN1 data and returns its root node. 136 | /// 137 | /// - Parameter data: ASN1 data to parse 138 | /// - Returns: Root ASN1 Node 139 | /// - Throws: A ParserError if anything goes wrong, or if an unknown node was encountered 140 | static func parse(data: Data) throws -> Node { 141 | let scanner = Scanner(data: data) 142 | let node = try parseNode(scanner: scanner) 143 | return node 144 | } 145 | 146 | /// Parses an ASN1 given an existing scanne. 147 | /// @warning: this will modify the state (ie: position) of the provided scanner. 148 | /// 149 | /// - Parameter scanner: Scanner to use to consume the data 150 | /// - Returns: Parsed node 151 | /// - Throws: A ParserError if anything goes wrong, or if an unknown node was encountered 152 | private static func parseNode(scanner: Scanner) throws -> Node { 153 | 154 | let firstByte = try scanner.consume(length: 1).firstByte 155 | 156 | // Sequence 157 | if firstByte == 0x30 { 158 | let length = try scanner.consumeLength() 159 | let data = try scanner.consume(length: length) 160 | let nodes = try parseSequence(data: data) 161 | return .sequence(nodes: nodes) 162 | } 163 | 164 | // Integer 165 | if firstByte == 0x02 { 166 | let length = try scanner.consumeLength() 167 | let data = try scanner.consume(length: length) 168 | return .integer(data: data) 169 | } 170 | 171 | // Object identifier 172 | if firstByte == 0x06 { 173 | let length = try scanner.consumeLength() 174 | let data = try scanner.consume(length: length) 175 | return .objectIdentifier(data: data) 176 | } 177 | 178 | // Null 179 | if firstByte == 0x05 { 180 | _ = try scanner.consume(length: 1) 181 | return .null 182 | } 183 | 184 | // Bit String 185 | if firstByte == 0x03 { 186 | let length = try scanner.consumeLength() 187 | 188 | // There's an extra byte (0x00) after the bit string length in all the keys I've encountered. 189 | // I couldn't find a specification that referenced this extra byte, but let's consume it and discard it. 190 | _ = try scanner.consume(length: 1) 191 | 192 | let data = try scanner.consume(length: length - 1) 193 | return .bitString(data: data) 194 | } 195 | 196 | // Octet String 197 | if firstByte == 0x04 { 198 | let length = try scanner.consumeLength() 199 | let data = try scanner.consume(length: length) 200 | return .octetString(data: data) 201 | } 202 | 203 | throw ParserError.invalidType(value: firstByte) 204 | } 205 | 206 | /// Parses an ASN1 sequence and returns its child nodes 207 | /// 208 | /// - Parameter data: ASN1 data 209 | /// - Returns: A list of ASN1 nodes 210 | /// - Throws: A ParserError if anything goes wrong, or if an unknown node was encountered 211 | private static func parseSequence(data: Data) throws -> [Node] { 212 | let scanner = Scanner(data: data) 213 | var nodes: [Node] = [] 214 | while !scanner.isComplete { 215 | let node = try parseNode(scanner: scanner) 216 | nodes.append(node) 217 | } 218 | return nodes 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /capillaryslack/SwiftyRSA+ObjC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftyRSA+ObjC.swift 3 | // SwiftyRSA 4 | // 5 | // Created by Lois Di Qual on 3/6/17. 6 | // Copyright © 2017 Scoop. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /// This files allows the ObjC runtime to access SwiftyRSA classes while keeping the swift code Swiftyish. 12 | /// Things like protocol extensions or throwing and returning booleans are not well supported by ObjC, so instead 13 | /// of giving access to the Swift classes directly, we're wrapping then their `_objc_*` counterpart. 14 | /// They are exposed under the same name to the ObjC runtime, and all methods are present – they're just delegated 15 | /// to the wrapped swift value. 16 | 17 | private protocol ObjcBridgeable { 18 | associatedtype SwiftType 19 | var swiftValue: SwiftType { get } 20 | init(swiftValue: SwiftType) 21 | } 22 | 23 | // MARK: – SwiftyRSA 24 | 25 | @objc(KeyPair) 26 | public class _objc_KeyPair: NSObject { // swiftlint:disable:this type_name 27 | 28 | @objc public let privateKey: _objc_PrivateKey 29 | @objc public let publicKey: _objc_PublicKey 30 | 31 | init(privateKey: _objc_PrivateKey, publicKey: _objc_PublicKey) { 32 | self.privateKey = privateKey 33 | self.publicKey = publicKey 34 | } 35 | } 36 | 37 | @objc(SwiftyRSA) 38 | public class _objc_SwiftyRSA: NSObject { // swiftlint:disable:this type_name 39 | 40 | @available(iOS 10.0, watchOS 3.0, tvOS 10.0, *) 41 | @objc public class func generateRSAKeyPair(sizeInBits size: Int,chainId:String) throws -> _objc_KeyPair { 42 | let (privateKey, publicKey) = try SwiftyRSA.generateRSAKeyPair(sizeInBits: size,chainId:chainId) 43 | return _objc_KeyPair( 44 | privateKey: _objc_PrivateKey(swiftValue: privateKey), 45 | publicKey: _objc_PublicKey(swiftValue: publicKey) 46 | ) 47 | } 48 | } 49 | 50 | // MARK: - PublicKey 51 | 52 | @objc(PublicKey) 53 | public class _objc_PublicKey: NSObject, Key, ObjcBridgeable { // swiftlint:disable:this type_name 54 | 55 | fileprivate let swiftValue: PublicKey 56 | 57 | @objc public var reference: SecKey { 58 | return swiftValue.reference 59 | } 60 | 61 | @objc public var originalData: Data? { 62 | return swiftValue.originalData 63 | } 64 | 65 | @objc public func pemString() throws -> String { 66 | return try swiftValue.pemString() 67 | } 68 | 69 | @objc public func data() throws -> Data { 70 | return try swiftValue.data() 71 | } 72 | 73 | @objc public func base64String() throws -> String { 74 | return try swiftValue.base64String() 75 | } 76 | 77 | required public init(swiftValue: PublicKey) { 78 | self.swiftValue = swiftValue 79 | } 80 | 81 | @objc required public init(data: Data) throws { 82 | self.swiftValue = try PublicKey(data: data) 83 | } 84 | 85 | @objc public required init(reference: SecKey) throws { 86 | self.swiftValue = try PublicKey(reference: reference) 87 | } 88 | 89 | @objc public required init(base64Encoded base64String: String) throws { 90 | self.swiftValue = try PublicKey(base64Encoded: base64String) 91 | } 92 | 93 | @objc public required init(pemEncoded pemString: String) throws { 94 | self.swiftValue = try PublicKey(pemEncoded: pemString) 95 | } 96 | 97 | @objc public required init(pemNamed pemName: String, in bundle: Bundle) throws { 98 | self.swiftValue = try PublicKey(pemNamed: pemName, in: bundle) 99 | } 100 | 101 | @objc public required init(derNamed derName: String, in bundle: Bundle) throws { 102 | self.swiftValue = try PublicKey(derNamed: derName, in: bundle) 103 | } 104 | 105 | @objc public static func publicKeys(pemEncoded pemString: String) -> [_objc_PublicKey] { 106 | return PublicKey.publicKeys(pemEncoded: pemString).map { _objc_PublicKey(swiftValue: $0) } 107 | } 108 | } 109 | 110 | // MARK: - PrivateKey 111 | 112 | @objc(PrivateKey) 113 | public class _objc_PrivateKey: NSObject, Key, ObjcBridgeable { // swiftlint:disable:this type_name 114 | 115 | fileprivate let swiftValue: PrivateKey 116 | 117 | @objc public var reference: SecKey { 118 | return swiftValue.reference 119 | } 120 | 121 | @objc public var originalData: Data? { 122 | return swiftValue.originalData 123 | } 124 | 125 | @objc public func pemString() throws -> String { 126 | return try swiftValue.pemString() 127 | } 128 | 129 | @objc public func data() throws -> Data { 130 | return try swiftValue.data() 131 | } 132 | 133 | @objc public func base64String() throws -> String { 134 | return try swiftValue.base64String() 135 | } 136 | 137 | public required init(swiftValue: PrivateKey) { 138 | self.swiftValue = swiftValue 139 | } 140 | 141 | @objc public required init(data: Data) throws { 142 | self.swiftValue = try PrivateKey(data: data) 143 | } 144 | 145 | @objc public required init(reference: SecKey) throws { 146 | self.swiftValue = try PrivateKey(reference: reference) 147 | } 148 | 149 | @objc public required init(base64Encoded base64String: String) throws { 150 | self.swiftValue = try PrivateKey(base64Encoded: base64String) 151 | } 152 | 153 | @objc public required init(pemEncoded pemString: String) throws { 154 | self.swiftValue = try PrivateKey(pemEncoded: pemString) 155 | } 156 | 157 | @objc public required init(pemNamed pemName: String, in bundle: Bundle) throws { 158 | self.swiftValue = try PrivateKey(pemNamed: pemName, in: bundle) 159 | } 160 | 161 | @objc public required init(derNamed derName: String, in bundle: Bundle) throws { 162 | self.swiftValue = try PrivateKey(derNamed: derName, in: bundle) 163 | } 164 | } 165 | 166 | // MARK: - VerificationResult 167 | 168 | @objc(VerificationResult) 169 | public class _objc_VerificationResult: NSObject { // swiftlint:disable:this type_name 170 | @objc public let isSuccessful: Bool 171 | init(isSuccessful: Bool) { 172 | self.isSuccessful = isSuccessful 173 | } 174 | } 175 | 176 | // MARK: - ClearMessage 177 | 178 | @objc(ClearMessage) 179 | public class _objc_ClearMessage: NSObject, Message, ObjcBridgeable { // swiftlint:disable:this type_name 180 | 181 | fileprivate let swiftValue: ClearMessage 182 | 183 | @objc public var base64String: String { 184 | return swiftValue.base64String 185 | } 186 | 187 | @objc public var data: Data { 188 | return swiftValue.data 189 | } 190 | 191 | public required init(swiftValue: ClearMessage) { 192 | self.swiftValue = swiftValue 193 | } 194 | 195 | @objc public required init(data: Data) { 196 | self.swiftValue = ClearMessage(data: data) 197 | } 198 | 199 | @objc public required init(string: String, using rawEncoding: UInt) throws { 200 | let encoding = String.Encoding(rawValue: rawEncoding) 201 | self.swiftValue = try ClearMessage(string: string, using: encoding) 202 | } 203 | 204 | @objc public required init(base64Encoded base64String: String) throws { 205 | self.swiftValue = try ClearMessage(base64Encoded: base64String) 206 | } 207 | 208 | @objc public func string(encoding rawEncoding: UInt) throws -> String { 209 | let encoding = String.Encoding(rawValue: rawEncoding) 210 | return try swiftValue.string(encoding: encoding) 211 | } 212 | 213 | @objc public func encrypted(with key: _objc_PublicKey, padding: Padding) throws -> _objc_EncryptedMessage { 214 | let encryptedMessage = try swiftValue.encrypted(with: key.swiftValue, padding: padding) 215 | return _objc_EncryptedMessage(swiftValue: encryptedMessage) 216 | } 217 | 218 | @objc public func signed(with key: _objc_PrivateKey, digestType: _objc_Signature.DigestType) throws -> _objc_Signature { 219 | let signature = try swiftValue.signed(with: key.swiftValue, digestType: digestType.swiftValue) 220 | return _objc_Signature(swiftValue: signature) 221 | } 222 | 223 | @objc public func verify(with key: _objc_PublicKey, signature: _objc_Signature, digestType: _objc_Signature.DigestType) throws -> _objc_VerificationResult { 224 | let isSuccessful = try swiftValue.verify(with: key.swiftValue, signature: signature.swiftValue, digestType: digestType.swiftValue) 225 | return _objc_VerificationResult(isSuccessful: isSuccessful) 226 | } 227 | } 228 | 229 | // MARK: - EncryptedMessage 230 | 231 | @objc(EncryptedMessage) 232 | public class _objc_EncryptedMessage: NSObject, Message, ObjcBridgeable { // swiftlint:disable:this type_name 233 | 234 | fileprivate let swiftValue: EncryptedMessage 235 | 236 | @objc public var base64String: String { 237 | return swiftValue.base64String 238 | } 239 | 240 | @objc public var data: Data { 241 | return swiftValue.data 242 | } 243 | 244 | public required init(swiftValue: EncryptedMessage) { 245 | self.swiftValue = swiftValue 246 | } 247 | 248 | @objc public required init(data: Data) { 249 | self.swiftValue = EncryptedMessage(data: data) 250 | } 251 | 252 | @objc public required init(base64Encoded base64String: String) throws { 253 | self.swiftValue = try EncryptedMessage(base64Encoded: base64String) 254 | } 255 | 256 | @objc public func decrypted(with key: _objc_PrivateKey, padding: Padding) throws -> _objc_ClearMessage { 257 | let clearMessage = try swiftValue.decrypted(with: key.swiftValue, padding: padding) 258 | return _objc_ClearMessage(swiftValue: clearMessage) 259 | } 260 | } 261 | 262 | // MARK: - Signature 263 | 264 | @objc(Signature) 265 | public class _objc_Signature: NSObject, ObjcBridgeable { // swiftlint:disable:this type_name 266 | 267 | @objc 268 | public enum DigestType: Int { 269 | case sha1 270 | case sha224 271 | case sha256 272 | case sha384 273 | case sha512 274 | 275 | fileprivate var swiftValue: Signature.DigestType { 276 | switch self { 277 | case .sha1: return .sha1 278 | case .sha224: return .sha224 279 | case .sha256: return .sha256 280 | case .sha384: return .sha384 281 | case .sha512: return .sha512 282 | } 283 | } 284 | } 285 | 286 | fileprivate let swiftValue: Signature 287 | 288 | @objc public var base64String: String { 289 | return swiftValue.base64String 290 | } 291 | 292 | @objc public var data: Data { 293 | return swiftValue.data 294 | } 295 | 296 | public required init(swiftValue: Signature) { 297 | self.swiftValue = swiftValue 298 | } 299 | 300 | @objc public init(data: Data) { 301 | self.swiftValue = Signature(data: data) 302 | } 303 | 304 | @objc public required init(base64Encoded base64String: String) throws { 305 | self.swiftValue = try Signature(base64Encoded: base64String) 306 | } 307 | } 308 | -------------------------------------------------------------------------------- /capillaryslack/SwiftyRSA.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftyRSA.swift 3 | // SwiftyRSA 4 | // 5 | // Created by Loïs Di Qual on 7/2/15. 6 | // 7 | // Copyright (c) 2015 Scoop Technologies, Inc. All rights reserved. 8 | // 9 | 10 | import Foundation 11 | import Security 12 | 13 | public typealias Padding = SecPadding 14 | 15 | public enum SwiftyRSA { 16 | 17 | static func base64String(pemEncoded pemString: String) throws -> String { 18 | let lines = pemString.components(separatedBy: "\n").filter { line in 19 | return !line.hasPrefix("-----BEGIN") && !line.hasPrefix("-----END") 20 | } 21 | 22 | guard lines.count != 0 else { 23 | throw SwiftyRSAError.pemDoesNotContainKey 24 | } 25 | 26 | return lines.joined(separator: "") 27 | } 28 | 29 | static func isValidKeyReference(_ reference: SecKey, forClass requiredClass: CFString) -> Bool { 30 | 31 | guard #available(iOS 10.0, *), #available(watchOS 3.0, *), #available(tvOS 10.0, *) else { 32 | return true 33 | } 34 | 35 | let attributes = SecKeyCopyAttributes(reference) as? [CFString: Any] 36 | guard let keyType = attributes?[kSecAttrKeyType] as? String, let keyClass = attributes?[kSecAttrKeyClass] as? String else { 37 | return false 38 | } 39 | 40 | let isRSA = keyType == (kSecAttrKeyTypeRSA as String) 41 | let isValidClass = keyClass == (requiredClass as String) 42 | return isRSA && isValidClass 43 | } 44 | 45 | static func format(keyData: Data, withPemType pemType: String) -> String { 46 | 47 | func split(_ str: String, byChunksOfLength length: Int) -> [String] { 48 | return stride(from: 0, to: str.count, by: length).map { index -> String in 49 | let startIndex = str.index(str.startIndex, offsetBy: index) 50 | let endIndex = str.index(startIndex, offsetBy: length, limitedBy: str.endIndex) ?? str.endIndex 51 | return String(str[startIndex.. Data { 70 | 71 | // On iOS+, we can use `SecKeyCopyExternalRepresentation` directly 72 | if #available(iOS 10.0, *), #available(watchOS 3.0, *), #available(tvOS 10.0, *) { 73 | 74 | var error: Unmanaged? 75 | let data = SecKeyCopyExternalRepresentation(reference, &error) 76 | guard let unwrappedData = data as Data? else { 77 | throw SwiftyRSAError.keyRepresentationFailed(error: error?.takeRetainedValue()) 78 | } 79 | return unwrappedData 80 | 81 | // On iOS 8/9, we need to add the key again to the keychain with a temporary tag, grab the data, 82 | // and delete the key again. 83 | } else { 84 | 85 | let temporaryTag = UUID().uuidString 86 | let addParams: [CFString: Any] = [ 87 | kSecValueRef: reference, 88 | kSecReturnData: true, 89 | kSecClass: kSecClassKey, 90 | kSecAttrApplicationTag: temporaryTag 91 | ] 92 | 93 | var data: AnyObject? 94 | let addStatus = SecItemAdd(addParams as CFDictionary, &data) 95 | guard let unwrappedData = data as? Data else { 96 | throw SwiftyRSAError.keyAddFailed(status: addStatus) 97 | } 98 | 99 | let deleteParams: [CFString: Any] = [ 100 | kSecClass: kSecClassKey, 101 | kSecAttrApplicationTag: temporaryTag 102 | ] 103 | 104 | _ = SecItemDelete(deleteParams as CFDictionary) 105 | 106 | return unwrappedData 107 | } 108 | } 109 | 110 | /// Will generate a new private and public key 111 | /// 112 | /// - Parameters: 113 | /// - size: Indicates the total number of bits in this cryptographic key 114 | /// - Returns: A touple of a private and public key 115 | /// - Throws: Throws and error if the tag cant be parsed or if keygeneration fails 116 | @available(iOS 10.0, watchOS 3.0, tvOS 10.0, *) 117 | public static func generateRSAKeyPair(sizeInBits size: Int,chainId:String) throws -> (privateKey: PrivateKey, publicKey: PublicKey) { 118 | return try generateRSAKeyPair(sizeInBits: size, applyUnitTestWorkaround: false,chainId: chainId) 119 | } 120 | 121 | @available(iOS 10.0, watchOS 3.0, tvOS 10.0, *) 122 | static func generateRSAKeyPair(sizeInBits size: Int, applyUnitTestWorkaround: Bool = false,chainId:String) throws -> (privateKey: PrivateKey, publicKey: PublicKey) { 123 | 124 | 125 | 126 | // @hack Don't store permanently when running unit tests, otherwise we'll get a key creation error (NSOSStatusErrorDomain -50) 127 | // @see http://www.openradar.me/36809637 128 | // @see https://stackoverflow.com/q/48414685/646960 129 | let isPermanent = applyUnitTestWorkaround ? false : true 130 | 131 | let attributes: [CFString: Any] = [ 132 | kSecAttrKeyType: kSecAttrKeyTypeRSA, 133 | kSecAttrKeySizeInBits: size, 134 | kSecPrivateKeyAttrs : [ 135 | kSecAttrIsPermanent: isPermanent, 136 | kSecAttrApplicationTag: "\(chainId).private" 137 | ], 138 | kSecPublicKeyAttrs : [ 139 | kSecAttrIsPermanent: isPermanent, 140 | kSecAttrApplicationTag: "\(chainId).public" 141 | ] 142 | ] 143 | 144 | var error: Unmanaged? 145 | guard let privKey = SecKeyCreateRandomKey(attributes as CFDictionary, &error), 146 | let pubKey = SecKeyCopyPublicKey(privKey) else { 147 | throw SwiftyRSAError.keyGenerationFailed(error: error?.takeRetainedValue()) 148 | } 149 | let privateKey = try PrivateKey(reference: privKey) 150 | let publicKey = try PublicKey(reference: pubKey) 151 | 152 | return (privateKey: privateKey, publicKey: publicKey) 153 | } 154 | 155 | static func addKey(_ keyData: Data, isPublic: Bool, tag: String) throws -> SecKey { 156 | 157 | let keyData = keyData 158 | 159 | guard let tagData = tag.data(using: .utf8) else { 160 | throw SwiftyRSAError.tagEncodingFailed 161 | } 162 | 163 | let keyClass = isPublic ? kSecAttrKeyClassPublic : kSecAttrKeyClassPrivate 164 | 165 | let sizeInBits = keyData.count * 8 166 | let keyDict: [CFString: Any] = [ 167 | kSecAttrKeyType: kSecAttrKeyTypeRSA, 168 | kSecAttrKeyClass: keyClass, 169 | kSecAttrKeySizeInBits: NSNumber(value: sizeInBits), 170 | kSecReturnPersistentRef: true, 171 | kSecAttrApplicationTag: tagData, 172 | ] 173 | 174 | var error: Unmanaged? 175 | guard let key = SecKeyCreateWithData(keyData as CFData, keyDict as CFDictionary, &error) else { 176 | throw SwiftyRSAError.keyCreateFailed(error: error?.takeRetainedValue()) 177 | } 178 | return key 179 | } 180 | 181 | //Check Keychain and get keys 182 | static func getKeysFromKeychain(chainId:String) -> Bool { 183 | let tagData = chainId.data(using: .utf8) 184 | let privateKey = getKeyTypeInKeyChain(tag: tagData!,keyClass: kSecAttrKeyClassPublic as String) 185 | let publicKey = getKeyTypeInKeyChain(tag: tagData!,keyClass: kSecAttrKeyClassPrivate as String) 186 | return ((privateKey != nil)&&(publicKey != nil)) 187 | } 188 | 189 | static func getKeyTypeInKeyChain(tag : Data,keyClass:String) -> SecKey? { 190 | let query: [CFString: Any] = [ 191 | kSecClass: kSecClassKey, 192 | kSecAttrKeyType: kSecAttrKeyTypeRSA, 193 | kSecAttrKeyClass: keyClass, 194 | kSecAttrApplicationTag: tag, 195 | kSecReturnRef: true, 196 | ] 197 | 198 | var result : AnyObject? 199 | let status = SecItemCopyMatching(query as CFDictionary, &result) 200 | 201 | if status == errSecSuccess { 202 | return result as! SecKey? 203 | } 204 | return nil 205 | } 206 | 207 | /** 208 | This method strips the x509 header from a provided ASN.1 DER key. 209 | If the key doesn't contain a header, the DER data is returned as is. 210 | 211 | Supported formats are: 212 | 213 | Headerless: 214 | SEQUENCE 215 | INTEGER (1024 or 2048 bit) -- modulo 216 | INTEGER -- public exponent 217 | 218 | With x509 header: 219 | SEQUENCE 220 | SEQUENCE 221 | OBJECT IDENTIFIER 1.2.840.113549.1.1.1 222 | NULL 223 | BIT STRING 224 | SEQUENCE 225 | INTEGER (1024 or 2048 bit) -- modulo 226 | INTEGER -- public exponent 227 | 228 | Example of headerless key: 229 | https://lapo.it/asn1js/#3082010A0282010100C1A0DFA367FBC2A5FD6ED5A071E02A4B0617E19C6B5AD11BB61192E78D212F10A7620084A3CED660894134D4E475BAD7786FA1D40878683FD1B7A1AD9C0542B7A666457A270159DAC40CE25B2EAE7CCD807D31AE725CA394F90FBB5C5BA500545B99C545A9FE08EFF00A5F23457633E1DB84ED5E908EF748A90F8DFCCAFF319CB0334705EA012AF15AA090D17A9330159C9AFC9275C610BB9B7C61317876DC7386C723885C100F774C19830F475AD1E9A9925F9CA9A69CE0181A214DF2EB75FD13E6A546B8C8ED699E33A8521242B7E42711066AEC22D25DD45D56F94D3170D6F2C25164D2DACED31C73963BA885ADCB706F40866B8266433ED5161DC50E4B3B0203010001 230 | 231 | Example of key with X509 header (notice the additional ASN.1 sequence): 232 | https://lapo.it/asn1js/#30819F300D06092A864886F70D010101050003818D0030818902818100D0674615A252ED3D75D2A3073A0A8A445F3188FD3BEB8BA8584F7299E391BDEC3427F287327414174997D147DD8CA62647427D73C9DA5504E0A3EED5274A1D50A1237D688486FADB8B82061675ABFA5E55B624095DB8790C6DBCAE83D6A8588C9A6635D7CF257ED1EDE18F04217D37908FD0CBB86B2C58D5F762E6207FF7B92D0203010001 233 | */ 234 | static func stripKeyHeader(keyData: Data) throws -> Data { 235 | 236 | let node: Asn1Parser.Node 237 | do { 238 | node = try Asn1Parser.parse(data: keyData) 239 | } catch { 240 | throw SwiftyRSAError.asn1ParsingFailed 241 | } 242 | 243 | // Ensure the raw data is an ASN1 sequence 244 | guard case .sequence(let nodes) = node else { 245 | throw SwiftyRSAError.invalidAsn1RootNode 246 | } 247 | 248 | // Detect whether the sequence only has integers, in which case it's a headerless key 249 | let onlyHasIntegers = nodes.filter { node -> Bool in 250 | if case .integer = node { 251 | return false 252 | } 253 | return true 254 | }.isEmpty 255 | 256 | // Headerless key 257 | if onlyHasIntegers { 258 | return keyData 259 | } 260 | 261 | // If last element of the sequence is a bit string, return its data 262 | if let last = nodes.last, case .bitString(let data) = last { 263 | return data 264 | } 265 | 266 | // If last element of the sequence is an octet string, return its data 267 | if let last = nodes.last, case .octetString(let data) = last { 268 | return data 269 | } 270 | 271 | // Unable to extract bit/octet string or raw integer sequence 272 | throw SwiftyRSAError.invalidAsn1Structure 273 | } 274 | 275 | /** 276 | This method prepend the x509 header to the given PublicKey data. 277 | If the key already contain a x509 header, the given data is returned as is. 278 | It letterally does the opposite of the previous method : 279 | From a given headerless key : 280 | SEQUENCE 281 | INTEGER (1024 or 2048 bit) -- modulo 282 | INTEGER -- public exponent 283 | the key is returned following the X509 header : 284 | SEQUENCE 285 | SEQUENCE 286 | OBJECT IDENTIFIER 1.2.840.113549.1.1.1 287 | NULL 288 | BIT STRING 289 | SEQUENCE 290 | INTEGER (1024 or 2048 bit) -- modulo 291 | INTEGER -- public exponent 292 | */ 293 | 294 | static func prependX509KeyHeader(keyData: Data) throws -> Data { 295 | if try keyData.isAnHeaderlessKey() { 296 | let x509certificate: Data = keyData.prependx509Header() 297 | return x509certificate 298 | } else if try keyData.hasX509Header() { 299 | return keyData 300 | } else { // invalideHeader 301 | throw SwiftyRSAError.x509CertificateFailed 302 | } 303 | } 304 | 305 | static func removeKey(tag: String) { 306 | 307 | guard let tagData = tag.data(using: .utf8) else { 308 | return 309 | } 310 | 311 | let keyRemoveDict: [CFString: Any] = [ 312 | kSecClass: kSecClassKey, 313 | kSecAttrKeyType: kSecAttrKeyTypeRSA, 314 | kSecAttrApplicationTag: tagData, 315 | ] 316 | 317 | SecItemDelete(keyRemoveDict as CFDictionary) 318 | } 319 | } 320 | 321 | #if !swift(>=4.1) 322 | extension Array { 323 | func compactMap(_ transform: (Element) throws -> ElementOfResult?) rethrows -> [ElementOfResult] { 324 | return try self.flatMap(transform) 325 | } 326 | } 327 | #endif 328 | 329 | #if !swift(>=4.0) 330 | extension NSTextCheckingResult { 331 | func range(at idx: Int) -> NSRange { 332 | return self.rangeAt(1) 333 | } 334 | } 335 | #endif 336 | -------------------------------------------------------------------------------- /capillaryslack/StoredKey.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StoredKey.swift 3 | // capillaryslack 4 | // 5 | // Created by Anmol Verma on 02/12/22. 6 | // 7 | 8 | import Foundation 9 | 10 | import CryptoKit 11 | 12 | // General approach to storing symmetric keys in the keychain, and code 13 | // snippets, are from here: 14 | // https://developer.apple.com/documentation/cryptokit/storing_cryptokit_keys_in_the_keychain 15 | // Declare protocol. 16 | protocol GenericPasswordConvertible: CustomStringConvertible { 17 | /// Creates a key from a raw representation. 18 | init(rawRepresentation data: D) throws where D: ContiguousBytes 19 | 20 | /// A raw representation of the key. 21 | var rawRepresentation: Data { get } 22 | } 23 | 24 | // Add extension that makes CryptoKey SymmetricKey satisfy the protocol. 25 | extension SymmetricKey: GenericPasswordConvertible { 26 | public var description: String { 27 | return "symmetrically" 28 | } 29 | 30 | init(rawRepresentation data: D) throws where D: ContiguousBytes { 31 | self.init(data: data) 32 | } 33 | 34 | var rawRepresentation: Data { 35 | return withUnsafeBytes{Data($0)} 36 | } 37 | } 38 | // End of first code to support storing CryptoKit symmetric key in the keychain. 39 | // Handy extension to get an error message from an OSStatus. 40 | extension OSStatus { 41 | var secErrorMessage: String { 42 | return (SecCopyErrorMessageString(self, nil) as String?) ?? "\(self)" 43 | } 44 | } 45 | 46 | // Extensions to make some pre-Swift classes conform to Encodable. 47 | extension NSNumber: Encodable { 48 | public func encode(to encoder: Encoder) throws { 49 | try Int(exactly: self).encode(to: encoder) 50 | } 51 | } 52 | 53 | extension CFNumber: Encodable { 54 | public func encode(to encoder: Encoder) throws { 55 | try (self as NSNumber).encode(to: encoder) 56 | } 57 | } 58 | extension CFString: Encodable { 59 | public func encode(to encoder: Encoder) throws { 60 | try (self as String).encode(to: encoder) 61 | } 62 | } 63 | 64 | protocol StoredKeyBasis { 65 | func storedKey() -> StoredKey 66 | } 67 | extension SecKey: StoredKeyBasis { 68 | func storedKey() -> StoredKey { return StoredKey(self) } 69 | } 70 | extension SymmetricKey: StoredKeyBasis { 71 | func storedKey() -> StoredKey { return StoredKey(self) } 72 | } 73 | 74 | // Main class in this file. 75 | class StoredKey { 76 | 77 | // Enumeration for different storage of keys supported by this class, either 78 | // as generic passwords in the keychain, or as keys in the keychain. The 79 | // keychain only stores private keys as keys, so symmetric keys must be 80 | // stored as generic passwords. 81 | enum Storage: String, CaseIterable { 82 | case generic, key 83 | 84 | var secClass:CFString { 85 | switch self { 86 | case .generic: 87 | return kSecClassGenericPassword 88 | case .key: 89 | return kSecClassKey 90 | } 91 | } 92 | } 93 | 94 | // Enumeration for descriptive names for types of key pair. 95 | // 96 | // The values for the key type attribute, kSecAttrKeyType, have slightly 97 | // strange behaviour. 98 | // 99 | // In the dictionary passed to SecKeyCreateRandomKey, the value of the 100 | // kSecAttrKeyType attribute is a CFString. However, the contents of the 101 | // CFString will be numeric. For example, kSecAttrKeyTypeRSA has the 102 | // value "42". See, for example: 103 | // https://opensource.apple.com/source/Security/Security-55471/sec/Security/SecItemConstants.c.auto.html 104 | // 105 | // In the dictionary returned from SecItemCopyMatching, the value of the 106 | // kSecAttrKeyType attribute will be a CFNumber instead. 107 | // 108 | private enum KeyType: String, CaseIterable { 109 | case RSA, EC 110 | 111 | var secAttrKeyType:CFString { 112 | switch self { 113 | case .RSA: 114 | return kSecAttrKeyTypeRSA 115 | case .EC: 116 | return kSecAttrKeyTypeECSECPrimeRandom 117 | } 118 | } 119 | // There is also a kSecAttrKeyTypeEC, which is deprecated. It has the 120 | // same value as kSecAttrKeyTypeECSECPrimeRandom. This means that 121 | // there's no way to tell the difference by matching. 122 | static func matching(_ secAttrKeyTypeValue:CFString) -> KeyType? { 123 | // self.allCases on the next line is enabled by CaseIterable in the 124 | // declaration. 125 | self.allCases.first(where: { 126 | $0.secAttrKeyType == secAttrKeyTypeValue 127 | }) 128 | } 129 | 130 | // Nested struct for a description tuple containing: 131 | // 132 | // - String, like "RSA" or "EC". 133 | // - Raw value from a keychain query, or keychain attribute 134 | // dictionary. 135 | // 136 | // In practice, the raw value will be CFNumber or CFString. 137 | struct Description:Encodable { 138 | let keyType:String 139 | let raw:AnyEncodable 140 | 141 | private init(keyType:String, raw:Encodable) { 142 | self.keyType = keyType 143 | self.raw = AnyEncodable(raw) 144 | } 145 | 146 | init(fromCopyAttribute specifier: Any) { 147 | // If the specifier is a number, compare numerically to the 148 | // numbers in each kSecAttrKeyType constant. 149 | if let typeNumber = specifier as? NSNumber, 150 | let typeInt = Int(exactly: typeNumber), 151 | let keyType = KeyType.allCases.first(where: { 152 | Int($0.secAttrKeyType as String) == typeInt 153 | }) 154 | { 155 | self.init(keyType: keyType.rawValue, raw: typeNumber) 156 | } 157 | // Otherwise, compare as a string. 158 | else if let typeString = specifier as? String { 159 | self.init( 160 | keyType: 161 | KeyType.matching(typeString as CFString)?.rawValue 162 | ?? typeString, 163 | raw: typeString 164 | ) 165 | } 166 | // Otherwise, go through a catch-all. 167 | else { 168 | self.init(keyType: "Unknown", raw: "\(specifier)") 169 | } 170 | } 171 | } 172 | } 173 | 174 | struct Deletion: Encodable { 175 | let deleted: [String] 176 | let notDeleted: [String:String] 177 | } 178 | 179 | // Clears the keychain and returns a summary of what storage types were 180 | // deleted or not deleted because of an error. 181 | static func deleteAll() -> Deletion { 182 | var deleted:[String] = [] 183 | var notDeleted:[String:String] = [:] 184 | 185 | for storage in Storage.allCases { 186 | // Query to find all items of this security class. 187 | let query: [CFString: Any] = [kSecClass: storage.secClass] 188 | let status = SecItemDelete(query as CFDictionary) 189 | if status == errSecSuccess || status == errSecItemNotFound { 190 | deleted.append(storage.rawValue) 191 | } 192 | else { 193 | notDeleted[storage.rawValue] = status.secErrorMessage 194 | } 195 | } 196 | 197 | return Deletion(deleted: deleted, notDeleted: notDeleted) 198 | } 199 | 200 | static func keysWithName(_ alias:String) throws -> [StoredKey] { 201 | return try Storage.allCases.flatMap {storage -> [StoredKey] in 202 | var query: [CFString: Any] = [ 203 | kSecClass: storage.secClass, 204 | kSecAttrLabel: alias, 205 | ] 206 | 207 | switch(storage) { 208 | case .generic: 209 | query[kSecReturnData] = true 210 | case .key: 211 | query[kSecReturnRef] = true 212 | } 213 | 214 | var itemRef: CFTypeRef? 215 | let status = SecItemCopyMatching(query as CFDictionary, &itemRef) 216 | 217 | guard status == errSecSuccess || status == errSecItemNotFound else { 218 | throw StoredKeyError(status, "Query \(query).") 219 | } 220 | 221 | // Set items to an NSArray of the return value, or an empty NSArray. 222 | let items = status == errSecSuccess 223 | ? (itemRef as! CFArray) as NSArray 224 | : NSArray() 225 | 226 | return items.map({item in 227 | switch(storage) { 228 | case .generic: 229 | return StoredKey(SymmetricKey(data: item as! Data)) 230 | case .key: 231 | return StoredKey(item as! SecKey) 232 | } 233 | }) 234 | } 235 | } 236 | 237 | // Dummy type to wrap any Encodable value. 238 | // 239 | // This is here because the following doesn't compile: 240 | // 241 | // public struct Description:Encodable { 242 | // let storage:String 243 | // let name:String 244 | // let type:String 245 | // let attributes:[String:Encodable] // This line is an error. 246 | // } 247 | // 248 | // It appears that there has to be an enum, struct or class wrapped around 249 | // the object that is Encodable. 250 | // 251 | struct AnyEncodable:Encodable { 252 | let encodable:Encodable 253 | 254 | init(_ encodable:Encodable) { 255 | self.encodable = encodable 256 | } 257 | 258 | func encode(to encoder: Encoder) throws { 259 | try encodable.encode(to: encoder) 260 | } 261 | } 262 | 263 | // Encodable representation of a key, as returned by a keychain query. 264 | public struct Description:Encodable { 265 | let storage:String 266 | let name:String 267 | let type:String 268 | let attributes:[String:AnyEncodable] 269 | 270 | init(_ storage:Storage, _ attributes:CFDictionary) { 271 | self.storage = storage.rawValue 272 | 273 | // Create a dictionary of normalised values. Some of the normalised 274 | // values are also used in the rest of the constructor. 275 | self.attributes = Description.normalise(cfAttributes: attributes) 276 | 277 | // `name` will be the kSecAttrLabel if it can be a String, or the 278 | // empty string otherwise. 279 | self.name = 280 | (attributes as NSDictionary)[kSecAttrLabel as String] as? String 281 | ?? "" 282 | 283 | // `type` will be a string derived by the KeyType.Description 284 | // constructor. 285 | let keyType:String 286 | if let element = self.attributes[kSecAttrKeyType as String] { 287 | if let description = element.encodable as? KeyType.Description { 288 | keyType = description.keyType 289 | } 290 | else { 291 | // Code reaches this point if there is somehow a value in 292 | // the normalised dictionary that isn't a 293 | // KeyType.Description, which shouldn't happen but just in 294 | // case. 295 | keyType = "\(element.encodable)" 296 | } 297 | } 298 | else { 299 | keyType = "" 300 | } 301 | self.type = keyType 302 | } 303 | 304 | static private func fallbackValue(_ rawValue:Any) -> Encodable { 305 | return rawValue as? NSNumber 306 | ?? (rawValue as? Encodable ?? "\(rawValue)") 307 | } 308 | 309 | static func normalise(cfAttributes:CFDictionary) -> [String:AnyEncodable] { 310 | // Keys in the attribute dictionary will sometimes be the short 311 | // names that are the underlying values of the various kSecAttr 312 | // constants. You can see a list of all the short names and 313 | // corresponding kSecAttr names in the Apple Open Source 314 | // SecItemConstants.c file. For example, here: 315 | // https://opensource.apple.com/source/Security/Security-55471/sec/Security/SecItemConstants.c.auto.html 316 | 317 | var returning: [String:AnyEncodable] = [:] 318 | for (rawKey, rawValue) in cfAttributes as NSDictionary { 319 | let value:Encodable 320 | 321 | if let key = rawKey as? String { 322 | // Check for known attributes with special handling first. 323 | if key == kSecAttrApplicationTag as String { 324 | if let rawData = rawValue as? Data { 325 | value = String(data: rawData, encoding: .utf8) 326 | } 327 | else { 328 | // If rawValue is a String already, or any other 329 | // Encodable, the fallbackValue will return it. 330 | value = fallbackValue(rawValue) 331 | } 332 | } 333 | else if key == kSecAttrKeyType as String { 334 | value = KeyType.Description(fromCopyAttribute: rawValue) 335 | } 336 | // 337 | // Key isn't a known value with special handling. 338 | else if let nsDictionary = rawValue as? NSDictionary { 339 | // Recursive call to preserve hierarchy, for example if 340 | // this is an attribute dictionary for a key pair. 341 | value = normalise(cfAttributes: nsDictionary) 342 | } 343 | else { 344 | value = fallbackValue(rawValue) 345 | } 346 | returning[key] = AnyEncodable(value) 347 | } 348 | else { 349 | // Code reaches this point if the key couldn't be cast to 350 | // String. This is a catch all. 351 | returning[String(describing: rawKey)] = 352 | AnyEncodable(fallbackValue(rawValue)) 353 | } 354 | } 355 | return returning 356 | } 357 | 358 | } 359 | 360 | static func describeAll() throws -> [Description] { 361 | return try Storage.allCases.flatMap {storage -> [Description] in 362 | let query: [CFString: Any] = [ 363 | kSecClass: storage.secClass, 364 | kSecReturnAttributes: true, 365 | kSecMatchLimit: kSecMatchLimitAll 366 | ] 367 | // Above query sets kSecMatchLimit: kSecMatchLimitAll so that the 368 | // results will be a CFArray. The type of each item in the array is 369 | // determined by which kSecReturn option is set. 370 | // 371 | // kSecReturnAttributes true 372 | // Gets a CFDictionary representation of each key. 373 | // 374 | // kSecReturnRef true 375 | // Would get a SecKey object for each key. A dictionary 376 | // representation can be generated from a SecKey by calling 377 | // SecKeyCopyAttributes(). However the resulting dictionary has only 378 | // a subset of the attributes. For example, it doesn't have these: 379 | // 380 | // - kSecAttrLabel 381 | // - kSecAttrApplicationTag 382 | // 383 | // kSecReturnData true 384 | // Gets a CFData instance for each key. From the reference documentation 385 | // it looks like the data should be a PKCS#1 representation. 386 | 387 | var itemRef: CFTypeRef? 388 | let status = SecItemCopyMatching(query as CFDictionary, &itemRef) 389 | 390 | // If SecItemCopyMatching failed, status will be a numeric error 391 | // code. To find out what a particular number means, you can look it 392 | // up here: 393 | // https://www.osstatus.com/search/results?platform=all&framework=all&search=errSec 394 | // That will get you the symbolic name. 395 | // 396 | // Symbolic names can be looked up in the official reference, here: 397 | // https://developer.apple.com/documentation/security/1542001-security_framework_result_codes 398 | // But it isn't searchable by number. 399 | // 400 | // This is how Jim found out that -25300 is errSecItemNotFound. 401 | 402 | guard status == errSecSuccess || status == errSecItemNotFound else { 403 | throw StoredKeyError(status) 404 | } 405 | 406 | // Set items to an NSArray of the return value, or an empty NSArray 407 | // in case of errSecItemNotFound. 408 | let items = status == errSecSuccess 409 | ? (itemRef as! CFArray) as NSArray 410 | : NSArray() 411 | 412 | return items.map { item -> Description in 413 | Description(storage, item as! CFDictionary) 414 | } 415 | } 416 | } 417 | 418 | private enum GenerationSentinelResult:String { 419 | case passed, failed, multipleKeys 420 | } 421 | 422 | private static func generationSentinel( 423 | _ basis:StoredKeyBasis, _ alias:String 424 | ) throws -> GenerationSentinelResult 425 | { 426 | let keys = try self.keysWithName(alias) 427 | if keys.count == 1 { 428 | let storedKey = basis.storedKey() 429 | let sentinel = "InMemorySentinel" 430 | let encrypted = try storedKey.encrypt(sentinel) 431 | let decrypted = try self.decrypt( 432 | encrypted, withFirstKeyNamed: alias) 433 | return sentinel == decrypted ? .passed : .failed 434 | } 435 | else { 436 | return .multipleKeys 437 | } 438 | } 439 | 440 | struct KeyGeneration:Encodable { 441 | let deletedFirst:Bool 442 | let sentinelCheck:String 443 | let summary:[String] 444 | let attributes:[String:AnyEncodable] 445 | } 446 | 447 | // Generate a symmetric key and store it in the keychain, as a generic 448 | // password. 449 | static func generateKey(withName alias:String) throws -> KeyGeneration { 450 | // First delete any generic key chain item with the same label. If you 451 | // don't, the add seems to fail as a duplicate. 452 | let deleteQuery:[CFString:Any] = [ 453 | kSecClass: kSecClassGenericPassword, 454 | kSecAttrLabel: alias, 455 | 456 | // Generic passwords in the keychain use the following two items as 457 | // identifying attributes. If you don't set them, a first keychain 458 | // item will still be stored, but a second keychain item will be 459 | // rejected as a duplicate. 460 | // TOTH: https://useyourloaf.com/blog/keychain-duplicate-item-when-adding-password/ 461 | kSecAttrAccount: "Account \(alias)", 462 | kSecAttrService: "Service \(alias)" 463 | 464 | ] 465 | let deleted = SecItemDelete(deleteQuery as CFDictionary) 466 | guard deleted == errSecSuccess || deleted == errSecItemNotFound else { 467 | throw StoredKeyError( 468 | deleted, "Failed SecItemDelete(\(deleteQuery)).") 469 | } 470 | 471 | // Generate the random symmetric key. 472 | let key = SymmetricKey(size: .bits256) 473 | 474 | // Merge in more query attributes, to create the add query. 475 | let addQuery = deleteQuery.merging([ 476 | kSecReturnAttributes: true, 477 | kSecValueData: key.rawRepresentation, 478 | ]) {(_, new) in new} 479 | 480 | var result: CFTypeRef? 481 | let added = SecItemAdd(addQuery as CFDictionary, &result) 482 | guard added == errSecSuccess else { 483 | throw StoredKeyError(added, "Failed SecItemAdd(\(addQuery),)") 484 | } 485 | 486 | // The KeyGeneration here is a little different to the generateKeyPair 487 | // return value. That's because this key is created in memory and then 488 | // put in the keychain with a query, as two steps. Key pair generation 489 | // is already in the keychain as a single step. 490 | return KeyGeneration( 491 | deletedFirst: deleted == errSecSuccess, 492 | sentinelCheck: try generationSentinel(key, alias).rawValue, 493 | summary: [String(describing:key)], 494 | attributes: 495 | Description.normalise(cfAttributes: result as! CFDictionary) 496 | ) 497 | } 498 | 499 | private static func tag(forAlias alias: String) -> (String, Data) { 500 | let tagString = "\(Bundle.main.bundleIdentifier ?? "").\(alias)" 501 | return (tagString, tagString.data(using: .utf8)!) 502 | } 503 | 504 | static func generateKeyPair(withName alias:String,isTest:Bool) throws 505 | { 506 | // Official code snippets are here: 507 | // https://developer.apple.com/documentation/security/certificate_key_and_trust_services/keys/generating_new_cryptographic_keys 508 | guard let tagSD = alias.data(using: .utf8) else { 509 | throw SwiftyRSAError.stringToDataConversionFailed 510 | } 511 | 512 | let attributes: [CFString: Any] = [ 513 | kSecAttrKeyType: kSecAttrKeyTypeRSA, 514 | kSecAttrKeySizeInBits: 2048, 515 | kSecPrivateKeyAttrs: [ 516 | kSecAttrIsPermanent: isTest, 517 | kSecAttrLabel: alias, 518 | kSecAttrApplicationTag: tagSD 519 | ] 520 | ] 521 | 522 | var error: Unmanaged? 523 | guard let secKey = SecKeyCreateRandomKey( 524 | attributes as CFDictionary, &error) else 525 | { 526 | throw error!.takeRetainedValue() as Error 527 | } 528 | 529 | 530 | } 531 | 532 | // Properties and methods of a StoredKey instance. It isn't necessary to use 533 | // StoredKey instances externally. The static methods, like 534 | // encypt(message withFirstKeyNamed:) for example, can be used instead. 535 | private let _storage:Storage 536 | let secKey:SecKey? 537 | let symmetricKey:SymmetricKey? 538 | 539 | var storage:String {_storage.rawValue} 540 | 541 | // Symmetric key constructor. 542 | init(_ symmetricKey:SymmetricKey) { 543 | _storage = .generic 544 | secKey = nil 545 | self.symmetricKey = symmetricKey 546 | } 547 | 548 | // Key pair constructor. The SecKey will be the private key. The 549 | // corresponding private key will be generated if needed in the 550 | // encryptBasedOnPrivateKey() method, below. 551 | init(_ secKey:SecKey) { 552 | _storage = .key 553 | self.secKey = secKey 554 | symmetricKey = nil 555 | } 556 | 557 | // Tuple for encrypted data and the algorithm. The algorithm is for 558 | // description only. It is nil in the symmetric key case. 559 | public struct Encrypted { 560 | let message:Data 561 | let algorithm:SecKeyAlgorithm? 562 | } 563 | 564 | // Instance methods for encryption and decryption. 565 | func encrypt(_ message:String) throws -> Encrypted 566 | { 567 | switch _storage { 568 | case .key: 569 | return try encryptBasedOnPrivateKey(message) 570 | case .generic: 571 | return try encryptWithSymmetricKey(message) 572 | } 573 | } 574 | 575 | // Instance methods for encryption and decryption. 576 | func encryptBytes(_ message:Data) throws -> Encrypted 577 | { 578 | switch _storage { 579 | case .key: 580 | return try encryptBasedOnPrivateKey(message) 581 | case .generic: 582 | return try encryptWithSymmetricKey(message) 583 | } 584 | } 585 | 586 | 587 | func decrypt(_ encrypted:Data) throws -> String { 588 | switch _storage { 589 | case .key: 590 | return try decryptWithPrivateKey(encrypted as CFData) 591 | case .generic: 592 | return try decryptWithSymmetricKey(encrypted) 593 | } 594 | } 595 | 596 | func decryptAsData(_ encrypted:Data) throws -> Data { 597 | switch _storage { 598 | case .key: 599 | return try decryptWithPrivateKeyAsData(encrypted as CFData) 600 | case .generic: 601 | return try decryptWithSymmetricKeyAsData(encrypted) 602 | } 603 | } 604 | 605 | func decrypt(_ encrypted:Encrypted) throws -> String { 606 | return try decrypt(encrypted.message) 607 | } 608 | 609 | private func encryptWithSymmetricKey(_ message:String) throws -> Encrypted { 610 | guard let box = try 611 | AES.GCM.seal( 612 | Data(message.utf8) as NSData, using: symmetricKey! 613 | ).combined else 614 | { 615 | throw StoredKeyError("Combined nil.") 616 | } 617 | return Encrypted(message:box, algorithm: nil) 618 | } 619 | 620 | private func encryptWithSymmetricKey(_ message:Data) throws -> Encrypted { 621 | guard let box = try 622 | AES.GCM.seal(message, using: symmetricKey!).combined else 623 | { 624 | throw StoredKeyError("Combined nil.") 625 | } 626 | return Encrypted(message:box, algorithm: nil) 627 | } 628 | 629 | private func decryptWithSymmetricKey(_ encrypted:Data) throws -> String { 630 | let sealed = try AES.GCM.SealedBox(combined: encrypted) 631 | let decryptedData = try AES.GCM.open(sealed, using: symmetricKey!) 632 | let message = 633 | String(data: decryptedData, encoding: .utf8) ?? "\(decryptedData)" 634 | return message 635 | } 636 | 637 | private func decryptWithSymmetricKeyAsData(_ encrypted:Data) throws -> Data { 638 | let sealed = try AES.GCM.SealedBox(combined: encrypted) 639 | let decryptedData = try AES.GCM.open(sealed, using: symmetricKey!) 640 | return decryptedData 641 | } 642 | 643 | // List of algorithms for public key encryption. 644 | private let algorithms:[SecKeyAlgorithm] = [ 645 | .eciesEncryptionStandardX963SHA1AESGCM, 646 | .rsaEncryptionPKCS1 647 | ] 648 | 649 | private func encryptBasedOnPrivateKey(_ message:Data) throws -> Encrypted 650 | { 651 | guard let publicKey = SecKeyCopyPublicKey(secKey!) else { 652 | throw StoredKeyError("No public key.") 653 | } 654 | 655 | guard let algorithm = algorithms.first( 656 | where: { SecKeyIsAlgorithmSupported(publicKey, .encrypt, $0)} 657 | ) else 658 | { 659 | throw StoredKeyError("No algorithms supported.") 660 | } 661 | 662 | var error: Unmanaged? 663 | guard let encryptedBytes = SecKeyCreateEncryptedData( 664 | publicKey, algorithm, message as CFData, &error) else { 665 | throw error!.takeRetainedValue() as Error 666 | } 667 | return Encrypted(message: encryptedBytes as Data, algorithm:algorithm) 668 | } 669 | 670 | private func encryptBasedOnPrivateKey(_ message:String) throws -> Encrypted 671 | { 672 | guard let publicKey = SecKeyCopyPublicKey(secKey!) else { 673 | throw StoredKeyError("No public key.") 674 | } 675 | 676 | guard let algorithm = algorithms.first( 677 | where: { SecKeyIsAlgorithmSupported(publicKey, .encrypt, $0)} 678 | ) else 679 | { 680 | throw StoredKeyError("No algorithms supported.") 681 | } 682 | 683 | var error: Unmanaged? 684 | guard let encryptedBytes = SecKeyCreateEncryptedData( 685 | publicKey, algorithm, Data(message.utf8) as CFData, &error) else { 686 | throw error!.takeRetainedValue() as Error 687 | } 688 | return Encrypted(message: encryptedBytes as Data, algorithm:algorithm) 689 | } 690 | 691 | private func decryptWithPrivateKey(_ encrypted:CFData) throws -> String { 692 | guard let publicKey = SecKeyCopyPublicKey(secKey!) else { 693 | throw StoredKeyError("No public key.") 694 | } 695 | guard let algorithm = algorithms.first( 696 | where: { SecKeyIsAlgorithmSupported(publicKey, .decrypt, $0)} 697 | ) else 698 | { 699 | throw StoredKeyError("No algorithms supported.") 700 | } 701 | 702 | var error: Unmanaged? 703 | guard let decryptedBytes = SecKeyCreateDecryptedData( 704 | secKey!, algorithm, encrypted, &error) else { 705 | throw error!.takeRetainedValue() as Error 706 | } 707 | 708 | let message = String( 709 | data: decryptedBytes as Data, encoding: .utf8) 710 | ?? "\(decryptedBytes)" 711 | return message 712 | } 713 | 714 | private func decryptWithPrivateKeyAsData(_ encrypted:CFData) throws -> Data { 715 | guard let publicKey = SecKeyCopyPublicKey(secKey!) else { 716 | throw StoredKeyError("No public key.") 717 | } 718 | 719 | 720 | guard let algorithm = algorithms.first( 721 | where: { SecKeyIsAlgorithmSupported(publicKey, .decrypt, $0)} 722 | ) else 723 | { 724 | throw StoredKeyError("No algorithms supported.") 725 | } 726 | 727 | var error: Unmanaged? 728 | guard let decryptedBytes = SecKeyCreateDecryptedData( 729 | secKey!, algorithm, encrypted, &error) else { 730 | throw error!.takeRetainedValue() as Error 731 | } 732 | 733 | return decryptedBytes as Data 734 | } 735 | 736 | // Static methods that work with a key alias instead of a StoredKey 737 | // instance. 738 | static func encrypt(_ message:String, withFirstKeyNamed alias:String) 739 | throws -> Encrypted 740 | { 741 | guard let key = try keysWithName(alias).first else { 742 | throw StoredKeyError(errSecItemNotFound) 743 | } 744 | return try key.encrypt(message) 745 | } 746 | 747 | static func decrypt(_ encrypted:Encrypted, withFirstKeyNamed alias:String) 748 | throws -> String 749 | { 750 | guard let key = try keysWithName(alias).first else { 751 | throw StoredKeyError(errSecItemNotFound) 752 | } 753 | return try key.decrypt(encrypted) 754 | } 755 | } 756 | 757 | extension Array { 758 | func inserting(_ element:Element, at index:Int) -> Array { 759 | var inserted = self 760 | inserted.insert(element, at: index) 761 | return inserted 762 | } 763 | } 764 | 765 | // Swift seems to have made it rather difficult to create a throw-able that 766 | // has a message that can be retrieved in the catch. So, there's a custom 767 | // class here. 768 | // 769 | // Having created a custom class anyway, it seemed like a code-saver to pack 770 | // it with convenience initialisers for an array of strings, variadic 771 | // strings, CFString, and OSStatus. 772 | public class StoredKeyError: Error, CustomStringConvertible { 773 | let _message:String 774 | 775 | public init(_ message:String) { 776 | self._message = message 777 | } 778 | public convenience init(_ message:[String]) { 779 | self.init(message.joined()) 780 | } 781 | public convenience init(_ message:String...) { 782 | self.init(message) 783 | } 784 | public convenience init(_ message:CFString) { 785 | self.init(NSString(string: message) as String) 786 | } 787 | public convenience init(_ osStatus:OSStatus, _ details:String...) { 788 | self.init(details.inserting(osStatus.secErrorMessage, at: 0)) 789 | } 790 | 791 | public var message: String { 792 | return self._message 793 | } 794 | 795 | public var localizedDescription: String { 796 | return self._message 797 | } 798 | 799 | public var description: String { 800 | return self._message 801 | } 802 | } 803 | -------------------------------------------------------------------------------- /capillaryslack.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 56; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 127F4D4EC65906F024C56E17 /* Pods_capillaryslack.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F90A840D802C8806BFB2866C /* Pods_capillaryslack.framework */; }; 11 | 89228A1C2939D6EF008A5D5C /* PublicKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89228A0D2939D6EF008A5D5C /* PublicKey.swift */; }; 12 | 89228A1D2939D6EF008A5D5C /* X509Certificate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89228A0E2939D6EF008A5D5C /* X509Certificate.swift */; }; 13 | 89228A1F2939D6EF008A5D5C /* EncryptedMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89228A102939D6EF008A5D5C /* EncryptedMessage.swift */; }; 14 | 89228A202939D6EF008A5D5C /* SwiftyRSA.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89228A112939D6EF008A5D5C /* SwiftyRSA.swift */; }; 15 | 89228A222939D6EF008A5D5C /* Key.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89228A132939D6EF008A5D5C /* Key.swift */; }; 16 | 89228A232939D6EF008A5D5C /* Message.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89228A142939D6EF008A5D5C /* Message.swift */; }; 17 | 89228A242939D6EF008A5D5C /* SwiftyRSAError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89228A152939D6EF008A5D5C /* SwiftyRSAError.swift */; }; 18 | 89228A252939D6EF008A5D5C /* PrivateKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89228A162939D6EF008A5D5C /* PrivateKey.swift */; }; 19 | 89228A262939D6EF008A5D5C /* Asn1Parser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89228A172939D6EF008A5D5C /* Asn1Parser.swift */; }; 20 | 89228A272939D6EF008A5D5C /* ClearMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89228A182939D6EF008A5D5C /* ClearMessage.swift */; }; 21 | 89228A292939D6EF008A5D5C /* SwiftyRSA+ObjC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89228A1A2939D6EF008A5D5C /* SwiftyRSA+ObjC.swift */; }; 22 | 89228A2A2939D6EF008A5D5C /* Signature.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89228A1B2939D6EF008A5D5C /* Signature.swift */; }; 23 | 89228A2C2939D789008A5D5C /* Data+SHA.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89228A2B2939D789008A5D5C /* Data+SHA.swift */; }; 24 | 89228A2E2939D907008A5D5C /* EncryptedData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89228A2D2939D907008A5D5C /* EncryptedData.swift */; }; 25 | 89AC2CB4293A221F00D600F0 /* sampleappApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89AC2CB3293A221F00D600F0 /* sampleappApp.swift */; }; 26 | 89AC2CB6293A221F00D600F0 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89AC2CB5293A221F00D600F0 /* ContentView.swift */; }; 27 | 89AC2CB8293A222000D600F0 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 89AC2CB7293A222000D600F0 /* Assets.xcassets */; }; 28 | 89AC2CBB293A222000D600F0 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 89AC2CBA293A222000D600F0 /* Preview Assets.xcassets */; }; 29 | 89AC2CC5293A222100D600F0 /* sampleappTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89AC2CC4293A222100D600F0 /* sampleappTests.swift */; }; 30 | 89AC2CCF293A222100D600F0 /* sampleappUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89AC2CCE293A222100D600F0 /* sampleappUITests.swift */; }; 31 | 89AC2CD1293A222100D600F0 /* sampleappUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89AC2CD0293A222100D600F0 /* sampleappUITestsLaunchTests.swift */; }; 32 | 89ADB4AE2939C83500BDE1E5 /* StoredKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89ADB4AD2939C83500BDE1E5 /* StoredKey.swift */; }; 33 | 89B7AD7829348BDB00EB4530 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 89B7AD7729348BDB00EB4530 /* Security.framework */; }; 34 | 89D1404F292E26CE000F8C51 /* capillaryslack.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 89D14044292E26CE000F8C51 /* capillaryslack.framework */; }; 35 | 89D14054292E26CE000F8C51 /* capillaryslackTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89D14053292E26CE000F8C51 /* capillaryslackTests.swift */; }; 36 | 89D14055292E26CE000F8C51 /* capillaryslack.h in Headers */ = {isa = PBXBuildFile; fileRef = 89D14047292E26CE000F8C51 /* capillaryslack.h */; settings = {ATTRIBUTES = (Public, ); }; }; 37 | 89D14074292E26F4000F8C51 /* capillaryios-Bridging-Header.h in Headers */ = {isa = PBXBuildFile; fileRef = 89D14062292E26F4000F8C51 /* capillaryios-Bridging-Header.h */; }; 38 | 89D14080292E26F4000F8C51 /* capillaryios.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89D1406E292E26F4000F8C51 /* capillaryios.swift */; }; 39 | /* End PBXBuildFile section */ 40 | 41 | /* Begin PBXContainerItemProxy section */ 42 | 89AC2CC1293A222100D600F0 /* PBXContainerItemProxy */ = { 43 | isa = PBXContainerItemProxy; 44 | containerPortal = 89D1403B292E26CE000F8C51 /* Project object */; 45 | proxyType = 1; 46 | remoteGlobalIDString = 89AC2CB0293A221F00D600F0; 47 | remoteInfo = sampleapp; 48 | }; 49 | 89AC2CCB293A222100D600F0 /* PBXContainerItemProxy */ = { 50 | isa = PBXContainerItemProxy; 51 | containerPortal = 89D1403B292E26CE000F8C51 /* Project object */; 52 | proxyType = 1; 53 | remoteGlobalIDString = 89AC2CB0293A221F00D600F0; 54 | remoteInfo = sampleapp; 55 | }; 56 | 89D14050292E26CE000F8C51 /* PBXContainerItemProxy */ = { 57 | isa = PBXContainerItemProxy; 58 | containerPortal = 89D1403B292E26CE000F8C51 /* Project object */; 59 | proxyType = 1; 60 | remoteGlobalIDString = 89D14043292E26CE000F8C51; 61 | remoteInfo = capillaryslack; 62 | }; 63 | /* End PBXContainerItemProxy section */ 64 | 65 | /* Begin PBXFileReference section */ 66 | 89228A0D2939D6EF008A5D5C /* PublicKey.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PublicKey.swift; sourceTree = ""; }; 67 | 89228A0E2939D6EF008A5D5C /* X509Certificate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = X509Certificate.swift; sourceTree = ""; }; 68 | 89228A102939D6EF008A5D5C /* EncryptedMessage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EncryptedMessage.swift; sourceTree = ""; }; 69 | 89228A112939D6EF008A5D5C /* SwiftyRSA.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftyRSA.swift; sourceTree = ""; }; 70 | 89228A132939D6EF008A5D5C /* Key.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Key.swift; sourceTree = ""; }; 71 | 89228A142939D6EF008A5D5C /* Message.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Message.swift; sourceTree = ""; }; 72 | 89228A152939D6EF008A5D5C /* SwiftyRSAError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftyRSAError.swift; sourceTree = ""; }; 73 | 89228A162939D6EF008A5D5C /* PrivateKey.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PrivateKey.swift; sourceTree = ""; }; 74 | 89228A172939D6EF008A5D5C /* Asn1Parser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Asn1Parser.swift; sourceTree = ""; }; 75 | 89228A182939D6EF008A5D5C /* ClearMessage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ClearMessage.swift; sourceTree = ""; }; 76 | 89228A1A2939D6EF008A5D5C /* SwiftyRSA+ObjC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SwiftyRSA+ObjC.swift"; sourceTree = ""; }; 77 | 89228A1B2939D6EF008A5D5C /* Signature.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Signature.swift; sourceTree = ""; }; 78 | 89228A2B2939D789008A5D5C /* Data+SHA.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Data+SHA.swift"; sourceTree = ""; }; 79 | 89228A2D2939D907008A5D5C /* EncryptedData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EncryptedData.swift; sourceTree = ""; }; 80 | 89AC2CB1293A221F00D600F0 /* sampleapp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = sampleapp.app; sourceTree = BUILT_PRODUCTS_DIR; }; 81 | 89AC2CB3293A221F00D600F0 /* sampleappApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = sampleappApp.swift; sourceTree = ""; }; 82 | 89AC2CB5293A221F00D600F0 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; 83 | 89AC2CB7293A222000D600F0 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 84 | 89AC2CBA293A222000D600F0 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; 85 | 89AC2CC0293A222100D600F0 /* sampleappTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = sampleappTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 86 | 89AC2CC4293A222100D600F0 /* sampleappTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = sampleappTests.swift; sourceTree = ""; }; 87 | 89AC2CCA293A222100D600F0 /* sampleappUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = sampleappUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 88 | 89AC2CCE293A222100D600F0 /* sampleappUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = sampleappUITests.swift; sourceTree = ""; }; 89 | 89AC2CD0293A222100D600F0 /* sampleappUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = sampleappUITestsLaunchTests.swift; sourceTree = ""; }; 90 | 89ADB4AD2939C83500BDE1E5 /* StoredKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoredKey.swift; sourceTree = ""; }; 91 | 89ADB4AF2939D1AE00BDE1E5 /* SwiftyRSA.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = SwiftyRSA.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 92 | 89ADB4B12939D1D200BDE1E5 /* SwiftyRSA.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = SwiftyRSA.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 93 | 89ADB4B52939D2BF00BDE1E5 /* SwiftyRSA.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = SwiftyRSA.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 94 | 89ADB4B92939D3DB00BDE1E5 /* SwiftyRSA.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = SwiftyRSA.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 95 | 89ADB4BB2939D4F300BDE1E5 /* SwiftyRSA.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = SwiftyRSA.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 96 | 89B7AD7729348BDB00EB4530 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX13.0.sdk/System/Library/Frameworks/Security.framework; sourceTree = DEVELOPER_DIR; }; 97 | 89D14044292E26CE000F8C51 /* capillaryslack.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = capillaryslack.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 98 | 89D14047292E26CE000F8C51 /* capillaryslack.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = capillaryslack.h; sourceTree = ""; }; 99 | 89D1404E292E26CE000F8C51 /* capillaryslackTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = capillaryslackTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 100 | 89D14053292E26CE000F8C51 /* capillaryslackTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = capillaryslackTests.swift; sourceTree = ""; }; 101 | 89D14062292E26F4000F8C51 /* capillaryios-Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "capillaryios-Bridging-Header.h"; sourceTree = ""; }; 102 | 89D1406E292E26F4000F8C51 /* capillaryios.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = capillaryios.swift; sourceTree = ""; }; 103 | E2EEF061DAE4B9D2455681FA /* Pods-capillaryslack.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-capillaryslack.debug.xcconfig"; path = "Target Support Files/Pods-capillaryslack/Pods-capillaryslack.debug.xcconfig"; sourceTree = ""; }; 104 | ED3EE160606629951939089C /* Pods-capillaryslack.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-capillaryslack.release.xcconfig"; path = "Target Support Files/Pods-capillaryslack/Pods-capillaryslack.release.xcconfig"; sourceTree = ""; }; 105 | F90A840D802C8806BFB2866C /* Pods_capillaryslack.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_capillaryslack.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 106 | /* End PBXFileReference section */ 107 | 108 | /* Begin PBXFrameworksBuildPhase section */ 109 | 89AC2CAE293A221F00D600F0 /* Frameworks */ = { 110 | isa = PBXFrameworksBuildPhase; 111 | buildActionMask = 2147483647; 112 | files = ( 113 | ); 114 | runOnlyForDeploymentPostprocessing = 0; 115 | }; 116 | 89AC2CBD293A222100D600F0 /* Frameworks */ = { 117 | isa = PBXFrameworksBuildPhase; 118 | buildActionMask = 2147483647; 119 | files = ( 120 | ); 121 | runOnlyForDeploymentPostprocessing = 0; 122 | }; 123 | 89AC2CC7293A222100D600F0 /* Frameworks */ = { 124 | isa = PBXFrameworksBuildPhase; 125 | buildActionMask = 2147483647; 126 | files = ( 127 | ); 128 | runOnlyForDeploymentPostprocessing = 0; 129 | }; 130 | 89D14041292E26CE000F8C51 /* Frameworks */ = { 131 | isa = PBXFrameworksBuildPhase; 132 | buildActionMask = 2147483647; 133 | files = ( 134 | 89B7AD7829348BDB00EB4530 /* Security.framework in Frameworks */, 135 | 127F4D4EC65906F024C56E17 /* Pods_capillaryslack.framework in Frameworks */, 136 | ); 137 | runOnlyForDeploymentPostprocessing = 0; 138 | }; 139 | 89D1404B292E26CE000F8C51 /* Frameworks */ = { 140 | isa = PBXFrameworksBuildPhase; 141 | buildActionMask = 2147483647; 142 | files = ( 143 | 89D1404F292E26CE000F8C51 /* capillaryslack.framework in Frameworks */, 144 | ); 145 | runOnlyForDeploymentPostprocessing = 0; 146 | }; 147 | /* End PBXFrameworksBuildPhase section */ 148 | 149 | /* Begin PBXGroup section */ 150 | 89AC2CB2293A221F00D600F0 /* sampleapp */ = { 151 | isa = PBXGroup; 152 | children = ( 153 | 89AC2CB3293A221F00D600F0 /* sampleappApp.swift */, 154 | 89AC2CB5293A221F00D600F0 /* ContentView.swift */, 155 | 89AC2CB7293A222000D600F0 /* Assets.xcassets */, 156 | 89AC2CB9293A222000D600F0 /* Preview Content */, 157 | ); 158 | path = sampleapp; 159 | sourceTree = ""; 160 | }; 161 | 89AC2CB9293A222000D600F0 /* Preview Content */ = { 162 | isa = PBXGroup; 163 | children = ( 164 | 89AC2CBA293A222000D600F0 /* Preview Assets.xcassets */, 165 | ); 166 | path = "Preview Content"; 167 | sourceTree = ""; 168 | }; 169 | 89AC2CC3293A222100D600F0 /* sampleappTests */ = { 170 | isa = PBXGroup; 171 | children = ( 172 | 89AC2CC4293A222100D600F0 /* sampleappTests.swift */, 173 | ); 174 | path = sampleappTests; 175 | sourceTree = ""; 176 | }; 177 | 89AC2CCD293A222100D600F0 /* sampleappUITests */ = { 178 | isa = PBXGroup; 179 | children = ( 180 | 89AC2CCE293A222100D600F0 /* sampleappUITests.swift */, 181 | 89AC2CD0293A222100D600F0 /* sampleappUITestsLaunchTests.swift */, 182 | ); 183 | path = sampleappUITests; 184 | sourceTree = ""; 185 | }; 186 | 89B7AD7629348BDB00EB4530 /* Frameworks */ = { 187 | isa = PBXGroup; 188 | children = ( 189 | 89ADB4BB2939D4F300BDE1E5 /* SwiftyRSA.framework */, 190 | 89ADB4B92939D3DB00BDE1E5 /* SwiftyRSA.framework */, 191 | 89ADB4B52939D2BF00BDE1E5 /* SwiftyRSA.framework */, 192 | 89ADB4B12939D1D200BDE1E5 /* SwiftyRSA.framework */, 193 | 89ADB4AF2939D1AE00BDE1E5 /* SwiftyRSA.framework */, 194 | 89B7AD7729348BDB00EB4530 /* Security.framework */, 195 | F90A840D802C8806BFB2866C /* Pods_capillaryslack.framework */, 196 | ); 197 | name = Frameworks; 198 | sourceTree = ""; 199 | }; 200 | 89D1403A292E26CE000F8C51 = { 201 | isa = PBXGroup; 202 | children = ( 203 | 89D14046292E26CE000F8C51 /* capillaryslack */, 204 | 89D14052292E26CE000F8C51 /* capillaryslackTests */, 205 | 89AC2CB2293A221F00D600F0 /* sampleapp */, 206 | 89AC2CC3293A222100D600F0 /* sampleappTests */, 207 | 89AC2CCD293A222100D600F0 /* sampleappUITests */, 208 | 89D14045292E26CE000F8C51 /* Products */, 209 | 89B7AD7629348BDB00EB4530 /* Frameworks */, 210 | BF8F8C358A8E91193EB48CE1 /* Pods */, 211 | ); 212 | sourceTree = ""; 213 | }; 214 | 89D14045292E26CE000F8C51 /* Products */ = { 215 | isa = PBXGroup; 216 | children = ( 217 | 89D14044292E26CE000F8C51 /* capillaryslack.framework */, 218 | 89D1404E292E26CE000F8C51 /* capillaryslackTests.xctest */, 219 | 89AC2CB1293A221F00D600F0 /* sampleapp.app */, 220 | 89AC2CC0293A222100D600F0 /* sampleappTests.xctest */, 221 | 89AC2CCA293A222100D600F0 /* sampleappUITests.xctest */, 222 | ); 223 | name = Products; 224 | sourceTree = ""; 225 | }; 226 | 89D14046292E26CE000F8C51 /* capillaryslack */ = { 227 | isa = PBXGroup; 228 | children = ( 229 | 89228A172939D6EF008A5D5C /* Asn1Parser.swift */, 230 | 89228A182939D6EF008A5D5C /* ClearMessage.swift */, 231 | 89228A102939D6EF008A5D5C /* EncryptedMessage.swift */, 232 | 89228A132939D6EF008A5D5C /* Key.swift */, 233 | 89228A142939D6EF008A5D5C /* Message.swift */, 234 | 89228A162939D6EF008A5D5C /* PrivateKey.swift */, 235 | 89228A0D2939D6EF008A5D5C /* PublicKey.swift */, 236 | 89228A1B2939D6EF008A5D5C /* Signature.swift */, 237 | 89228A112939D6EF008A5D5C /* SwiftyRSA.swift */, 238 | 89228A1A2939D6EF008A5D5C /* SwiftyRSA+ObjC.swift */, 239 | 89228A152939D6EF008A5D5C /* SwiftyRSAError.swift */, 240 | 89228A0E2939D6EF008A5D5C /* X509Certificate.swift */, 241 | 89D14062292E26F4000F8C51 /* capillaryios-Bridging-Header.h */, 242 | 89D1406E292E26F4000F8C51 /* capillaryios.swift */, 243 | 89D14047292E26CE000F8C51 /* capillaryslack.h */, 244 | 89ADB4AD2939C83500BDE1E5 /* StoredKey.swift */, 245 | 89228A2B2939D789008A5D5C /* Data+SHA.swift */, 246 | 89228A2D2939D907008A5D5C /* EncryptedData.swift */, 247 | ); 248 | path = capillaryslack; 249 | sourceTree = ""; 250 | }; 251 | 89D14052292E26CE000F8C51 /* capillaryslackTests */ = { 252 | isa = PBXGroup; 253 | children = ( 254 | 89D14053292E26CE000F8C51 /* capillaryslackTests.swift */, 255 | ); 256 | path = capillaryslackTests; 257 | sourceTree = ""; 258 | }; 259 | BF8F8C358A8E91193EB48CE1 /* Pods */ = { 260 | isa = PBXGroup; 261 | children = ( 262 | E2EEF061DAE4B9D2455681FA /* Pods-capillaryslack.debug.xcconfig */, 263 | ED3EE160606629951939089C /* Pods-capillaryslack.release.xcconfig */, 264 | ); 265 | path = Pods; 266 | sourceTree = ""; 267 | }; 268 | /* End PBXGroup section */ 269 | 270 | /* Begin PBXHeadersBuildPhase section */ 271 | 89D1403F292E26CE000F8C51 /* Headers */ = { 272 | isa = PBXHeadersBuildPhase; 273 | buildActionMask = 2147483647; 274 | files = ( 275 | 89D14055292E26CE000F8C51 /* capillaryslack.h in Headers */, 276 | 89D14074292E26F4000F8C51 /* capillaryios-Bridging-Header.h in Headers */, 277 | ); 278 | runOnlyForDeploymentPostprocessing = 0; 279 | }; 280 | /* End PBXHeadersBuildPhase section */ 281 | 282 | /* Begin PBXNativeTarget section */ 283 | 89AC2CB0293A221F00D600F0 /* sampleapp */ = { 284 | isa = PBXNativeTarget; 285 | buildConfigurationList = 89AC2CD8293A222100D600F0 /* Build configuration list for PBXNativeTarget "sampleapp" */; 286 | buildPhases = ( 287 | 89AC2CAD293A221F00D600F0 /* Sources */, 288 | 89AC2CAE293A221F00D600F0 /* Frameworks */, 289 | 89AC2CAF293A221F00D600F0 /* Resources */, 290 | ); 291 | buildRules = ( 292 | ); 293 | dependencies = ( 294 | ); 295 | name = sampleapp; 296 | productName = sampleapp; 297 | productReference = 89AC2CB1293A221F00D600F0 /* sampleapp.app */; 298 | productType = "com.apple.product-type.application"; 299 | }; 300 | 89AC2CBF293A222100D600F0 /* sampleappTests */ = { 301 | isa = PBXNativeTarget; 302 | buildConfigurationList = 89AC2CD9293A222100D600F0 /* Build configuration list for PBXNativeTarget "sampleappTests" */; 303 | buildPhases = ( 304 | 89AC2CBC293A222100D600F0 /* Sources */, 305 | 89AC2CBD293A222100D600F0 /* Frameworks */, 306 | 89AC2CBE293A222100D600F0 /* Resources */, 307 | ); 308 | buildRules = ( 309 | ); 310 | dependencies = ( 311 | 89AC2CC2293A222100D600F0 /* PBXTargetDependency */, 312 | ); 313 | name = sampleappTests; 314 | productName = sampleappTests; 315 | productReference = 89AC2CC0293A222100D600F0 /* sampleappTests.xctest */; 316 | productType = "com.apple.product-type.bundle.unit-test"; 317 | }; 318 | 89AC2CC9293A222100D600F0 /* sampleappUITests */ = { 319 | isa = PBXNativeTarget; 320 | buildConfigurationList = 89AC2CDA293A222100D600F0 /* Build configuration list for PBXNativeTarget "sampleappUITests" */; 321 | buildPhases = ( 322 | 89AC2CC6293A222100D600F0 /* Sources */, 323 | 89AC2CC7293A222100D600F0 /* Frameworks */, 324 | 89AC2CC8293A222100D600F0 /* Resources */, 325 | ); 326 | buildRules = ( 327 | ); 328 | dependencies = ( 329 | 89AC2CCC293A222100D600F0 /* PBXTargetDependency */, 330 | ); 331 | name = sampleappUITests; 332 | productName = sampleappUITests; 333 | productReference = 89AC2CCA293A222100D600F0 /* sampleappUITests.xctest */; 334 | productType = "com.apple.product-type.bundle.ui-testing"; 335 | }; 336 | 89D14043292E26CE000F8C51 /* capillaryslack */ = { 337 | isa = PBXNativeTarget; 338 | buildConfigurationList = 89D14058292E26CE000F8C51 /* Build configuration list for PBXNativeTarget "capillaryslack" */; 339 | buildPhases = ( 340 | E9EBC27367D6C1E80624487F /* [CP] Check Pods Manifest.lock */, 341 | 89D1403F292E26CE000F8C51 /* Headers */, 342 | 89D14040292E26CE000F8C51 /* Sources */, 343 | 89D14041292E26CE000F8C51 /* Frameworks */, 344 | 89D14042292E26CE000F8C51 /* Resources */, 345 | ); 346 | buildRules = ( 347 | ); 348 | dependencies = ( 349 | ); 350 | name = capillaryslack; 351 | productName = capillaryslack; 352 | productReference = 89D14044292E26CE000F8C51 /* capillaryslack.framework */; 353 | productType = "com.apple.product-type.framework"; 354 | }; 355 | 89D1404D292E26CE000F8C51 /* capillaryslackTests */ = { 356 | isa = PBXNativeTarget; 357 | buildConfigurationList = 89D1405B292E26CE000F8C51 /* Build configuration list for PBXNativeTarget "capillaryslackTests" */; 358 | buildPhases = ( 359 | 89D1404A292E26CE000F8C51 /* Sources */, 360 | 89D1404B292E26CE000F8C51 /* Frameworks */, 361 | 89D1404C292E26CE000F8C51 /* Resources */, 362 | ); 363 | buildRules = ( 364 | ); 365 | dependencies = ( 366 | 89D14051292E26CE000F8C51 /* PBXTargetDependency */, 367 | ); 368 | name = capillaryslackTests; 369 | productName = capillaryslackTests; 370 | productReference = 89D1404E292E26CE000F8C51 /* capillaryslackTests.xctest */; 371 | productType = "com.apple.product-type.bundle.unit-test"; 372 | }; 373 | /* End PBXNativeTarget section */ 374 | 375 | /* Begin PBXProject section */ 376 | 89D1403B292E26CE000F8C51 /* Project object */ = { 377 | isa = PBXProject; 378 | attributes = { 379 | BuildIndependentTargetsInParallel = 1; 380 | LastSwiftUpdateCheck = 1410; 381 | LastUpgradeCheck = 1410; 382 | TargetAttributes = { 383 | 89AC2CB0293A221F00D600F0 = { 384 | CreatedOnToolsVersion = 14.1; 385 | }; 386 | 89AC2CBF293A222100D600F0 = { 387 | CreatedOnToolsVersion = 14.1; 388 | TestTargetID = 89AC2CB0293A221F00D600F0; 389 | }; 390 | 89AC2CC9293A222100D600F0 = { 391 | CreatedOnToolsVersion = 14.1; 392 | TestTargetID = 89AC2CB0293A221F00D600F0; 393 | }; 394 | 89D14043292E26CE000F8C51 = { 395 | CreatedOnToolsVersion = 14.1; 396 | LastSwiftMigration = 1410; 397 | }; 398 | 89D1404D292E26CE000F8C51 = { 399 | CreatedOnToolsVersion = 14.1; 400 | }; 401 | }; 402 | }; 403 | buildConfigurationList = 89D1403E292E26CE000F8C51 /* Build configuration list for PBXProject "capillaryslack" */; 404 | compatibilityVersion = "Xcode 14.0"; 405 | developmentRegion = en; 406 | hasScannedForEncodings = 0; 407 | knownRegions = ( 408 | en, 409 | Base, 410 | ); 411 | mainGroup = 89D1403A292E26CE000F8C51; 412 | productRefGroup = 89D14045292E26CE000F8C51 /* Products */; 413 | projectDirPath = ""; 414 | projectRoot = ""; 415 | targets = ( 416 | 89D14043292E26CE000F8C51 /* capillaryslack */, 417 | 89D1404D292E26CE000F8C51 /* capillaryslackTests */, 418 | 89AC2CB0293A221F00D600F0 /* sampleapp */, 419 | 89AC2CBF293A222100D600F0 /* sampleappTests */, 420 | 89AC2CC9293A222100D600F0 /* sampleappUITests */, 421 | ); 422 | }; 423 | /* End PBXProject section */ 424 | 425 | /* Begin PBXResourcesBuildPhase section */ 426 | 89AC2CAF293A221F00D600F0 /* Resources */ = { 427 | isa = PBXResourcesBuildPhase; 428 | buildActionMask = 2147483647; 429 | files = ( 430 | 89AC2CBB293A222000D600F0 /* Preview Assets.xcassets in Resources */, 431 | 89AC2CB8293A222000D600F0 /* Assets.xcassets in Resources */, 432 | ); 433 | runOnlyForDeploymentPostprocessing = 0; 434 | }; 435 | 89AC2CBE293A222100D600F0 /* Resources */ = { 436 | isa = PBXResourcesBuildPhase; 437 | buildActionMask = 2147483647; 438 | files = ( 439 | ); 440 | runOnlyForDeploymentPostprocessing = 0; 441 | }; 442 | 89AC2CC8293A222100D600F0 /* Resources */ = { 443 | isa = PBXResourcesBuildPhase; 444 | buildActionMask = 2147483647; 445 | files = ( 446 | ); 447 | runOnlyForDeploymentPostprocessing = 0; 448 | }; 449 | 89D14042292E26CE000F8C51 /* Resources */ = { 450 | isa = PBXResourcesBuildPhase; 451 | buildActionMask = 2147483647; 452 | files = ( 453 | ); 454 | runOnlyForDeploymentPostprocessing = 0; 455 | }; 456 | 89D1404C292E26CE000F8C51 /* Resources */ = { 457 | isa = PBXResourcesBuildPhase; 458 | buildActionMask = 2147483647; 459 | files = ( 460 | ); 461 | runOnlyForDeploymentPostprocessing = 0; 462 | }; 463 | /* End PBXResourcesBuildPhase section */ 464 | 465 | /* Begin PBXShellScriptBuildPhase section */ 466 | E9EBC27367D6C1E80624487F /* [CP] Check Pods Manifest.lock */ = { 467 | isa = PBXShellScriptBuildPhase; 468 | buildActionMask = 2147483647; 469 | files = ( 470 | ); 471 | inputFileListPaths = ( 472 | ); 473 | inputPaths = ( 474 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 475 | "${PODS_ROOT}/Manifest.lock", 476 | ); 477 | name = "[CP] Check Pods Manifest.lock"; 478 | outputFileListPaths = ( 479 | ); 480 | outputPaths = ( 481 | "$(DERIVED_FILE_DIR)/Pods-capillaryslack-checkManifestLockResult.txt", 482 | ); 483 | runOnlyForDeploymentPostprocessing = 0; 484 | shellPath = /bin/sh; 485 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 486 | showEnvVarsInLog = 0; 487 | }; 488 | /* End PBXShellScriptBuildPhase section */ 489 | 490 | /* Begin PBXSourcesBuildPhase section */ 491 | 89AC2CAD293A221F00D600F0 /* Sources */ = { 492 | isa = PBXSourcesBuildPhase; 493 | buildActionMask = 2147483647; 494 | files = ( 495 | 89AC2CB6293A221F00D600F0 /* ContentView.swift in Sources */, 496 | 89AC2CB4293A221F00D600F0 /* sampleappApp.swift in Sources */, 497 | ); 498 | runOnlyForDeploymentPostprocessing = 0; 499 | }; 500 | 89AC2CBC293A222100D600F0 /* Sources */ = { 501 | isa = PBXSourcesBuildPhase; 502 | buildActionMask = 2147483647; 503 | files = ( 504 | 89AC2CC5293A222100D600F0 /* sampleappTests.swift in Sources */, 505 | ); 506 | runOnlyForDeploymentPostprocessing = 0; 507 | }; 508 | 89AC2CC6293A222100D600F0 /* Sources */ = { 509 | isa = PBXSourcesBuildPhase; 510 | buildActionMask = 2147483647; 511 | files = ( 512 | 89AC2CD1293A222100D600F0 /* sampleappUITestsLaunchTests.swift in Sources */, 513 | 89AC2CCF293A222100D600F0 /* sampleappUITests.swift in Sources */, 514 | ); 515 | runOnlyForDeploymentPostprocessing = 0; 516 | }; 517 | 89D14040292E26CE000F8C51 /* Sources */ = { 518 | isa = PBXSourcesBuildPhase; 519 | buildActionMask = 2147483647; 520 | files = ( 521 | 89ADB4AE2939C83500BDE1E5 /* StoredKey.swift in Sources */, 522 | 89228A222939D6EF008A5D5C /* Key.swift in Sources */, 523 | 89228A242939D6EF008A5D5C /* SwiftyRSAError.swift in Sources */, 524 | 89228A262939D6EF008A5D5C /* Asn1Parser.swift in Sources */, 525 | 89228A1C2939D6EF008A5D5C /* PublicKey.swift in Sources */, 526 | 89D14080292E26F4000F8C51 /* capillaryios.swift in Sources */, 527 | 89228A2E2939D907008A5D5C /* EncryptedData.swift in Sources */, 528 | 89228A272939D6EF008A5D5C /* ClearMessage.swift in Sources */, 529 | 89228A1F2939D6EF008A5D5C /* EncryptedMessage.swift in Sources */, 530 | 89228A2C2939D789008A5D5C /* Data+SHA.swift in Sources */, 531 | 89228A1D2939D6EF008A5D5C /* X509Certificate.swift in Sources */, 532 | 89228A232939D6EF008A5D5C /* Message.swift in Sources */, 533 | 89228A202939D6EF008A5D5C /* SwiftyRSA.swift in Sources */, 534 | 89228A2A2939D6EF008A5D5C /* Signature.swift in Sources */, 535 | 89228A292939D6EF008A5D5C /* SwiftyRSA+ObjC.swift in Sources */, 536 | 89228A252939D6EF008A5D5C /* PrivateKey.swift in Sources */, 537 | ); 538 | runOnlyForDeploymentPostprocessing = 0; 539 | }; 540 | 89D1404A292E26CE000F8C51 /* Sources */ = { 541 | isa = PBXSourcesBuildPhase; 542 | buildActionMask = 2147483647; 543 | files = ( 544 | 89D14054292E26CE000F8C51 /* capillaryslackTests.swift in Sources */, 545 | ); 546 | runOnlyForDeploymentPostprocessing = 0; 547 | }; 548 | /* End PBXSourcesBuildPhase section */ 549 | 550 | /* Begin PBXTargetDependency section */ 551 | 89AC2CC2293A222100D600F0 /* PBXTargetDependency */ = { 552 | isa = PBXTargetDependency; 553 | target = 89AC2CB0293A221F00D600F0 /* sampleapp */; 554 | targetProxy = 89AC2CC1293A222100D600F0 /* PBXContainerItemProxy */; 555 | }; 556 | 89AC2CCC293A222100D600F0 /* PBXTargetDependency */ = { 557 | isa = PBXTargetDependency; 558 | target = 89AC2CB0293A221F00D600F0 /* sampleapp */; 559 | targetProxy = 89AC2CCB293A222100D600F0 /* PBXContainerItemProxy */; 560 | }; 561 | 89D14051292E26CE000F8C51 /* PBXTargetDependency */ = { 562 | isa = PBXTargetDependency; 563 | target = 89D14043292E26CE000F8C51 /* capillaryslack */; 564 | targetProxy = 89D14050292E26CE000F8C51 /* PBXContainerItemProxy */; 565 | }; 566 | /* End PBXTargetDependency section */ 567 | 568 | /* Begin XCBuildConfiguration section */ 569 | 89AC2CD2293A222100D600F0 /* Debug */ = { 570 | isa = XCBuildConfiguration; 571 | buildSettings = { 572 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 573 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 574 | CODE_SIGN_STYLE = Automatic; 575 | CURRENT_PROJECT_VERSION = 1; 576 | DEVELOPMENT_ASSET_PATHS = "\"sampleapp/Preview Content\""; 577 | DEVELOPMENT_TEAM = P3R865242N; 578 | ENABLE_PREVIEWS = YES; 579 | GENERATE_INFOPLIST_FILE = YES; 580 | INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; 581 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; 582 | INFOPLIST_KEY_UILaunchScreen_Generation = YES; 583 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 584 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 585 | IPHONEOS_DEPLOYMENT_TARGET = 16.1; 586 | LD_RUNPATH_SEARCH_PATHS = ( 587 | "$(inherited)", 588 | "@executable_path/Frameworks", 589 | ); 590 | MARKETING_VERSION = 1.0; 591 | PRODUCT_BUNDLE_IDENTIFIER = dev.baseio.sampleapp; 592 | PRODUCT_NAME = "$(TARGET_NAME)"; 593 | SWIFT_EMIT_LOC_STRINGS = YES; 594 | SWIFT_VERSION = 5.0; 595 | TARGETED_DEVICE_FAMILY = "1,2"; 596 | }; 597 | name = Debug; 598 | }; 599 | 89AC2CD3293A222100D600F0 /* Release */ = { 600 | isa = XCBuildConfiguration; 601 | buildSettings = { 602 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 603 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 604 | CODE_SIGN_STYLE = Automatic; 605 | CURRENT_PROJECT_VERSION = 1; 606 | DEVELOPMENT_ASSET_PATHS = "\"sampleapp/Preview Content\""; 607 | DEVELOPMENT_TEAM = P3R865242N; 608 | ENABLE_PREVIEWS = YES; 609 | GENERATE_INFOPLIST_FILE = YES; 610 | INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; 611 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; 612 | INFOPLIST_KEY_UILaunchScreen_Generation = YES; 613 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 614 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 615 | IPHONEOS_DEPLOYMENT_TARGET = 16.1; 616 | LD_RUNPATH_SEARCH_PATHS = ( 617 | "$(inherited)", 618 | "@executable_path/Frameworks", 619 | ); 620 | MARKETING_VERSION = 1.0; 621 | PRODUCT_BUNDLE_IDENTIFIER = dev.baseio.sampleapp; 622 | PRODUCT_NAME = "$(TARGET_NAME)"; 623 | SWIFT_EMIT_LOC_STRINGS = YES; 624 | SWIFT_VERSION = 5.0; 625 | TARGETED_DEVICE_FAMILY = "1,2"; 626 | }; 627 | name = Release; 628 | }; 629 | 89AC2CD4293A222100D600F0 /* Debug */ = { 630 | isa = XCBuildConfiguration; 631 | buildSettings = { 632 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 633 | BUNDLE_LOADER = "$(TEST_HOST)"; 634 | CODE_SIGN_STYLE = Automatic; 635 | CURRENT_PROJECT_VERSION = 1; 636 | DEVELOPMENT_TEAM = P3R865242N; 637 | GENERATE_INFOPLIST_FILE = YES; 638 | IPHONEOS_DEPLOYMENT_TARGET = 16.1; 639 | MARKETING_VERSION = 1.0; 640 | PRODUCT_BUNDLE_IDENTIFIER = dev.baseio.sampleappTests; 641 | PRODUCT_NAME = "$(TARGET_NAME)"; 642 | SWIFT_EMIT_LOC_STRINGS = NO; 643 | SWIFT_VERSION = 5.0; 644 | TARGETED_DEVICE_FAMILY = "1,2"; 645 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/sampleapp.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/sampleapp"; 646 | }; 647 | name = Debug; 648 | }; 649 | 89AC2CD5293A222100D600F0 /* Release */ = { 650 | isa = XCBuildConfiguration; 651 | buildSettings = { 652 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 653 | BUNDLE_LOADER = "$(TEST_HOST)"; 654 | CODE_SIGN_STYLE = Automatic; 655 | CURRENT_PROJECT_VERSION = 1; 656 | DEVELOPMENT_TEAM = P3R865242N; 657 | GENERATE_INFOPLIST_FILE = YES; 658 | IPHONEOS_DEPLOYMENT_TARGET = 16.1; 659 | MARKETING_VERSION = 1.0; 660 | PRODUCT_BUNDLE_IDENTIFIER = dev.baseio.sampleappTests; 661 | PRODUCT_NAME = "$(TARGET_NAME)"; 662 | SWIFT_EMIT_LOC_STRINGS = NO; 663 | SWIFT_VERSION = 5.0; 664 | TARGETED_DEVICE_FAMILY = "1,2"; 665 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/sampleapp.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/sampleapp"; 666 | }; 667 | name = Release; 668 | }; 669 | 89AC2CD6293A222100D600F0 /* Debug */ = { 670 | isa = XCBuildConfiguration; 671 | buildSettings = { 672 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 673 | CODE_SIGN_STYLE = Automatic; 674 | CURRENT_PROJECT_VERSION = 1; 675 | DEVELOPMENT_TEAM = P3R865242N; 676 | GENERATE_INFOPLIST_FILE = YES; 677 | IPHONEOS_DEPLOYMENT_TARGET = 16.1; 678 | MARKETING_VERSION = 1.0; 679 | PRODUCT_BUNDLE_IDENTIFIER = dev.baseio.sampleappUITests; 680 | PRODUCT_NAME = "$(TARGET_NAME)"; 681 | SWIFT_EMIT_LOC_STRINGS = NO; 682 | SWIFT_VERSION = 5.0; 683 | TARGETED_DEVICE_FAMILY = "1,2"; 684 | TEST_TARGET_NAME = sampleapp; 685 | }; 686 | name = Debug; 687 | }; 688 | 89AC2CD7293A222100D600F0 /* Release */ = { 689 | isa = XCBuildConfiguration; 690 | buildSettings = { 691 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 692 | CODE_SIGN_STYLE = Automatic; 693 | CURRENT_PROJECT_VERSION = 1; 694 | DEVELOPMENT_TEAM = P3R865242N; 695 | GENERATE_INFOPLIST_FILE = YES; 696 | IPHONEOS_DEPLOYMENT_TARGET = 16.1; 697 | MARKETING_VERSION = 1.0; 698 | PRODUCT_BUNDLE_IDENTIFIER = dev.baseio.sampleappUITests; 699 | PRODUCT_NAME = "$(TARGET_NAME)"; 700 | SWIFT_EMIT_LOC_STRINGS = NO; 701 | SWIFT_VERSION = 5.0; 702 | TARGETED_DEVICE_FAMILY = "1,2"; 703 | TEST_TARGET_NAME = sampleapp; 704 | }; 705 | name = Release; 706 | }; 707 | 89D14056292E26CE000F8C51 /* Debug */ = { 708 | isa = XCBuildConfiguration; 709 | buildSettings = { 710 | ALWAYS_SEARCH_USER_PATHS = NO; 711 | CLANG_ANALYZER_NONNULL = YES; 712 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 713 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; 714 | CLANG_ENABLE_MODULES = YES; 715 | CLANG_ENABLE_OBJC_ARC = YES; 716 | CLANG_ENABLE_OBJC_WEAK = YES; 717 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 718 | CLANG_WARN_BOOL_CONVERSION = YES; 719 | CLANG_WARN_COMMA = YES; 720 | CLANG_WARN_CONSTANT_CONVERSION = YES; 721 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 722 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 723 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 724 | CLANG_WARN_EMPTY_BODY = YES; 725 | CLANG_WARN_ENUM_CONVERSION = YES; 726 | CLANG_WARN_INFINITE_RECURSION = YES; 727 | CLANG_WARN_INT_CONVERSION = YES; 728 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 729 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 730 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 731 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 732 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 733 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 734 | CLANG_WARN_STRICT_PROTOTYPES = YES; 735 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 736 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 737 | CLANG_WARN_UNREACHABLE_CODE = YES; 738 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 739 | COPY_PHASE_STRIP = NO; 740 | CURRENT_PROJECT_VERSION = 1; 741 | DEBUG_INFORMATION_FORMAT = dwarf; 742 | ENABLE_STRICT_OBJC_MSGSEND = YES; 743 | ENABLE_TESTABILITY = YES; 744 | GCC_C_LANGUAGE_STANDARD = gnu11; 745 | GCC_DYNAMIC_NO_PIC = NO; 746 | GCC_NO_COMMON_BLOCKS = YES; 747 | GCC_OPTIMIZATION_LEVEL = 0; 748 | GCC_PREPROCESSOR_DEFINITIONS = ( 749 | "DEBUG=1", 750 | "$(inherited)", 751 | ); 752 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 753 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 754 | GCC_WARN_UNDECLARED_SELECTOR = YES; 755 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 756 | GCC_WARN_UNUSED_FUNCTION = YES; 757 | GCC_WARN_UNUSED_VARIABLE = YES; 758 | IPHONEOS_DEPLOYMENT_TARGET = 14.0; 759 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 760 | MTL_FAST_MATH = YES; 761 | ONLY_ACTIVE_ARCH = YES; 762 | SDKROOT = iphoneos; 763 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 764 | SWIFT_OBJC_BRIDGING_HEADER = ""; 765 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 766 | VERSIONING_SYSTEM = "apple-generic"; 767 | VERSION_INFO_PREFIX = ""; 768 | }; 769 | name = Debug; 770 | }; 771 | 89D14057292E26CE000F8C51 /* Release */ = { 772 | isa = XCBuildConfiguration; 773 | buildSettings = { 774 | ALWAYS_SEARCH_USER_PATHS = NO; 775 | CLANG_ANALYZER_NONNULL = YES; 776 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 777 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; 778 | CLANG_ENABLE_MODULES = YES; 779 | CLANG_ENABLE_OBJC_ARC = YES; 780 | CLANG_ENABLE_OBJC_WEAK = YES; 781 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 782 | CLANG_WARN_BOOL_CONVERSION = YES; 783 | CLANG_WARN_COMMA = YES; 784 | CLANG_WARN_CONSTANT_CONVERSION = YES; 785 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 786 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 787 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 788 | CLANG_WARN_EMPTY_BODY = YES; 789 | CLANG_WARN_ENUM_CONVERSION = YES; 790 | CLANG_WARN_INFINITE_RECURSION = YES; 791 | CLANG_WARN_INT_CONVERSION = YES; 792 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 793 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 794 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 795 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 796 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 797 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 798 | CLANG_WARN_STRICT_PROTOTYPES = YES; 799 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 800 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 801 | CLANG_WARN_UNREACHABLE_CODE = YES; 802 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 803 | COPY_PHASE_STRIP = NO; 804 | CURRENT_PROJECT_VERSION = 1; 805 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 806 | ENABLE_NS_ASSERTIONS = NO; 807 | ENABLE_STRICT_OBJC_MSGSEND = YES; 808 | GCC_C_LANGUAGE_STANDARD = gnu11; 809 | GCC_NO_COMMON_BLOCKS = YES; 810 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 811 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 812 | GCC_WARN_UNDECLARED_SELECTOR = YES; 813 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 814 | GCC_WARN_UNUSED_FUNCTION = YES; 815 | GCC_WARN_UNUSED_VARIABLE = YES; 816 | IPHONEOS_DEPLOYMENT_TARGET = 14.0; 817 | MTL_ENABLE_DEBUG_INFO = NO; 818 | MTL_FAST_MATH = YES; 819 | SDKROOT = iphoneos; 820 | SWIFT_COMPILATION_MODE = wholemodule; 821 | SWIFT_OBJC_BRIDGING_HEADER = ""; 822 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 823 | VALIDATE_PRODUCT = YES; 824 | VERSIONING_SYSTEM = "apple-generic"; 825 | VERSION_INFO_PREFIX = ""; 826 | }; 827 | name = Release; 828 | }; 829 | 89D14059292E26CE000F8C51 /* Debug */ = { 830 | isa = XCBuildConfiguration; 831 | baseConfigurationReference = E2EEF061DAE4B9D2455681FA /* Pods-capillaryslack.debug.xcconfig */; 832 | buildSettings = { 833 | CLANG_ENABLE_MODULES = YES; 834 | CODE_SIGN_STYLE = Automatic; 835 | CURRENT_PROJECT_VERSION = 1; 836 | DEFINES_MODULE = YES; 837 | DEVELOPMENT_TEAM = P3R865242N; 838 | DYLIB_COMPATIBILITY_VERSION = 1; 839 | DYLIB_CURRENT_VERSION = 1; 840 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 841 | GENERATE_INFOPLIST_FILE = YES; 842 | INFOPLIST_KEY_NSHumanReadableCopyright = ""; 843 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 844 | IPHONEOS_DEPLOYMENT_TARGET = 14.0; 845 | LD_RUNPATH_SEARCH_PATHS = ( 846 | "$(inherited)", 847 | "@executable_path/Frameworks", 848 | "@loader_path/Frameworks", 849 | ); 850 | MARKETING_VERSION = 1.0; 851 | PRODUCT_BUNDLE_IDENTIFIER = dev.baseio.capillaryslack; 852 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 853 | SKIP_INSTALL = YES; 854 | SWIFT_EMIT_LOC_STRINGS = YES; 855 | SWIFT_OBJC_BRIDGING_HEADER = ""; 856 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 857 | SWIFT_VERSION = 5.0; 858 | TARGETED_DEVICE_FAMILY = "1,2"; 859 | }; 860 | name = Debug; 861 | }; 862 | 89D1405A292E26CE000F8C51 /* Release */ = { 863 | isa = XCBuildConfiguration; 864 | baseConfigurationReference = ED3EE160606629951939089C /* Pods-capillaryslack.release.xcconfig */; 865 | buildSettings = { 866 | CLANG_ENABLE_MODULES = YES; 867 | CODE_SIGN_STYLE = Automatic; 868 | CURRENT_PROJECT_VERSION = 1; 869 | DEFINES_MODULE = YES; 870 | DEVELOPMENT_TEAM = P3R865242N; 871 | DYLIB_COMPATIBILITY_VERSION = 1; 872 | DYLIB_CURRENT_VERSION = 1; 873 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 874 | GENERATE_INFOPLIST_FILE = YES; 875 | INFOPLIST_KEY_NSHumanReadableCopyright = ""; 876 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 877 | IPHONEOS_DEPLOYMENT_TARGET = 14.0; 878 | LD_RUNPATH_SEARCH_PATHS = ( 879 | "$(inherited)", 880 | "@executable_path/Frameworks", 881 | "@loader_path/Frameworks", 882 | ); 883 | MARKETING_VERSION = 1.0; 884 | PRODUCT_BUNDLE_IDENTIFIER = dev.baseio.capillaryslack; 885 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 886 | SKIP_INSTALL = YES; 887 | SWIFT_EMIT_LOC_STRINGS = YES; 888 | SWIFT_OBJC_BRIDGING_HEADER = ""; 889 | SWIFT_VERSION = 5.0; 890 | TARGETED_DEVICE_FAMILY = "1,2"; 891 | }; 892 | name = Release; 893 | }; 894 | 89D1405C292E26CE000F8C51 /* Debug */ = { 895 | isa = XCBuildConfiguration; 896 | buildSettings = { 897 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 898 | "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; 899 | CODE_SIGN_STYLE = Automatic; 900 | CURRENT_PROJECT_VERSION = 1; 901 | DEVELOPMENT_TEAM = 2PPFC26FQA; 902 | GENERATE_INFOPLIST_FILE = YES; 903 | MARKETING_VERSION = 1.0; 904 | PRODUCT_BUNDLE_IDENTIFIER = dev.baseio.capillaryslackTests; 905 | PRODUCT_NAME = "$(TARGET_NAME)"; 906 | SWIFT_EMIT_LOC_STRINGS = NO; 907 | SWIFT_VERSION = 5.0; 908 | TARGETED_DEVICE_FAMILY = "1,2"; 909 | }; 910 | name = Debug; 911 | }; 912 | 89D1405D292E26CE000F8C51 /* Release */ = { 913 | isa = XCBuildConfiguration; 914 | buildSettings = { 915 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 916 | "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; 917 | CODE_SIGN_STYLE = Automatic; 918 | CURRENT_PROJECT_VERSION = 1; 919 | DEVELOPMENT_TEAM = 2PPFC26FQA; 920 | GENERATE_INFOPLIST_FILE = YES; 921 | MARKETING_VERSION = 1.0; 922 | PRODUCT_BUNDLE_IDENTIFIER = dev.baseio.capillaryslackTests; 923 | PRODUCT_NAME = "$(TARGET_NAME)"; 924 | SWIFT_EMIT_LOC_STRINGS = NO; 925 | SWIFT_VERSION = 5.0; 926 | TARGETED_DEVICE_FAMILY = "1,2"; 927 | }; 928 | name = Release; 929 | }; 930 | /* End XCBuildConfiguration section */ 931 | 932 | /* Begin XCConfigurationList section */ 933 | 89AC2CD8293A222100D600F0 /* Build configuration list for PBXNativeTarget "sampleapp" */ = { 934 | isa = XCConfigurationList; 935 | buildConfigurations = ( 936 | 89AC2CD2293A222100D600F0 /* Debug */, 937 | 89AC2CD3293A222100D600F0 /* Release */, 938 | ); 939 | defaultConfigurationIsVisible = 0; 940 | defaultConfigurationName = Release; 941 | }; 942 | 89AC2CD9293A222100D600F0 /* Build configuration list for PBXNativeTarget "sampleappTests" */ = { 943 | isa = XCConfigurationList; 944 | buildConfigurations = ( 945 | 89AC2CD4293A222100D600F0 /* Debug */, 946 | 89AC2CD5293A222100D600F0 /* Release */, 947 | ); 948 | defaultConfigurationIsVisible = 0; 949 | defaultConfigurationName = Release; 950 | }; 951 | 89AC2CDA293A222100D600F0 /* Build configuration list for PBXNativeTarget "sampleappUITests" */ = { 952 | isa = XCConfigurationList; 953 | buildConfigurations = ( 954 | 89AC2CD6293A222100D600F0 /* Debug */, 955 | 89AC2CD7293A222100D600F0 /* Release */, 956 | ); 957 | defaultConfigurationIsVisible = 0; 958 | defaultConfigurationName = Release; 959 | }; 960 | 89D1403E292E26CE000F8C51 /* Build configuration list for PBXProject "capillaryslack" */ = { 961 | isa = XCConfigurationList; 962 | buildConfigurations = ( 963 | 89D14056292E26CE000F8C51 /* Debug */, 964 | 89D14057292E26CE000F8C51 /* Release */, 965 | ); 966 | defaultConfigurationIsVisible = 0; 967 | defaultConfigurationName = Release; 968 | }; 969 | 89D14058292E26CE000F8C51 /* Build configuration list for PBXNativeTarget "capillaryslack" */ = { 970 | isa = XCConfigurationList; 971 | buildConfigurations = ( 972 | 89D14059292E26CE000F8C51 /* Debug */, 973 | 89D1405A292E26CE000F8C51 /* Release */, 974 | ); 975 | defaultConfigurationIsVisible = 0; 976 | defaultConfigurationName = Release; 977 | }; 978 | 89D1405B292E26CE000F8C51 /* Build configuration list for PBXNativeTarget "capillaryslackTests" */ = { 979 | isa = XCConfigurationList; 980 | buildConfigurations = ( 981 | 89D1405C292E26CE000F8C51 /* Debug */, 982 | 89D1405D292E26CE000F8C51 /* Release */, 983 | ); 984 | defaultConfigurationIsVisible = 0; 985 | defaultConfigurationName = Release; 986 | }; 987 | /* End XCConfigurationList section */ 988 | }; 989 | rootObject = 89D1403B292E26CE000F8C51 /* Project object */; 990 | } 991 | --------------------------------------------------------------------------------