├── 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 |
--------------------------------------------------------------------------------