mirror of
https://github.com/maxgoedjen/secretive.git
synced 2024-11-23 05:57:07 +00:00
parent
ee3e844519
commit
22d9b37d63
@ -0,0 +1,31 @@
|
|||||||
|
# ````SecretKit````
|
||||||
|
|
||||||
|
SecretKit is a collection of protocols describing secrets and stores.
|
||||||
|
|
||||||
|
## Topics
|
||||||
|
|
||||||
|
### Base Protocols
|
||||||
|
|
||||||
|
- ``Secret``
|
||||||
|
- ``SecretStore``
|
||||||
|
- ``SecretStoreModifiable``
|
||||||
|
|
||||||
|
### Store List
|
||||||
|
|
||||||
|
- ``SecretStoreList``
|
||||||
|
|
||||||
|
### Type Erasers
|
||||||
|
|
||||||
|
- ``AnySecret``
|
||||||
|
- ``AnySecretStore``
|
||||||
|
- ``AnySecretStoreModifiable``
|
||||||
|
|
||||||
|
### OpenSSH
|
||||||
|
|
||||||
|
- ``OpenSSHKeyWriter``
|
||||||
|
- ``OpenSSHReader``
|
||||||
|
|
||||||
|
### Signing Process
|
||||||
|
|
||||||
|
- ``SignedData``
|
||||||
|
- ``SigningRequestProvenance``
|
@ -1,5 +1,6 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
/// Type eraser for Secret.
|
||||||
public struct AnySecret: Secret {
|
public struct AnySecret: Secret {
|
||||||
|
|
||||||
let base: Any
|
let base: Any
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import Combine
|
import Combine
|
||||||
|
|
||||||
|
/// Type eraser for SecretStore.
|
||||||
public class AnySecretStore: SecretStore {
|
public class AnySecretStore: SecretStore {
|
||||||
|
|
||||||
let base: Any
|
let base: Any
|
||||||
|
@ -1,24 +1,31 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import CryptoKit
|
import CryptoKit
|
||||||
|
|
||||||
// For the moment, only supports ecdsa-sha2-nistp256 and ecdsa-sha2-nistp386 keys
|
/// Generates OpenSSH representations of Secrets.
|
||||||
public struct OpenSSHKeyWriter {
|
public struct OpenSSHKeyWriter {
|
||||||
|
|
||||||
|
/// Initializes the writer.
|
||||||
public init() {
|
public init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Generates an OpenSSH data payload identifying the secret.
|
||||||
|
/// - Returns: OpenSSH data payload identifying the secret.
|
||||||
public func data<SecretType: Secret>(secret: SecretType) -> Data {
|
public func data<SecretType: Secret>(secret: SecretType) -> Data {
|
||||||
lengthAndData(of: curveType(for: secret.algorithm, length: secret.keySize).data(using: .utf8)!) +
|
lengthAndData(of: curveType(for: secret.algorithm, length: secret.keySize).data(using: .utf8)!) +
|
||||||
lengthAndData(of: curveIdentifier(for: secret.algorithm, length: secret.keySize).data(using: .utf8)!) +
|
lengthAndData(of: curveIdentifier(for: secret.algorithm, length: secret.keySize).data(using: .utf8)!) +
|
||||||
lengthAndData(of: secret.publicKey)
|
lengthAndData(of: secret.publicKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Generates an OpenSSH string representation of the secret.
|
||||||
|
/// - Returns: OpenSSH string representation of the secret.
|
||||||
public func openSSHString<SecretType: Secret>(secret: SecretType, comment: String? = nil) -> String {
|
public func openSSHString<SecretType: Secret>(secret: SecretType, comment: String? = nil) -> String {
|
||||||
[curveType(for: secret.algorithm, length: secret.keySize), data(secret: secret).base64EncodedString(), comment]
|
[curveType(for: secret.algorithm, length: secret.keySize), data(secret: secret).base64EncodedString(), comment]
|
||||||
.compactMap { $0 }
|
.compactMap { $0 }
|
||||||
.joined(separator: " ")
|
.joined(separator: " ")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Generates an OpenSSH SHA256 fingerprint string.
|
||||||
|
/// - Returns: OpenSSH SHA256 fingerprint string.
|
||||||
public func openSSHSHA256Fingerprint<SecretType: Secret>(secret: SecretType) -> String {
|
public func openSSHSHA256Fingerprint<SecretType: Secret>(secret: SecretType) -> String {
|
||||||
// OpenSSL format seems to strip the padding at the end.
|
// OpenSSL format seems to strip the padding at the end.
|
||||||
let base64 = Data(SHA256.hash(data: data(secret: secret))).base64EncodedString()
|
let base64 = Data(SHA256.hash(data: data(secret: secret))).base64EncodedString()
|
||||||
@ -27,6 +34,8 @@ public struct OpenSSHKeyWriter {
|
|||||||
return "SHA256:\(cleaned)"
|
return "SHA256:\(cleaned)"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Generates an OpenSSH MD5 fingerprint string.
|
||||||
|
/// - Returns: OpenSSH MD5 fingerprint string.
|
||||||
public func openSSHMD5Fingerprint<SecretType: Secret>(secret: SecretType) -> String {
|
public func openSSHMD5Fingerprint<SecretType: Secret>(secret: SecretType) -> String {
|
||||||
Insecure.MD5.hash(data: data(secret: secret))
|
Insecure.MD5.hash(data: data(secret: secret))
|
||||||
.compactMap { ("0" + String($0, radix: 16, uppercase: false)).suffix(2) }
|
.compactMap { ("0" + String($0, radix: 16, uppercase: false)).suffix(2) }
|
||||||
@ -37,23 +46,37 @@ public struct OpenSSHKeyWriter {
|
|||||||
|
|
||||||
extension OpenSSHKeyWriter {
|
extension OpenSSHKeyWriter {
|
||||||
|
|
||||||
|
/// Creates an OpenSSH protocol style data object, which has a length header, followed by the data payload.
|
||||||
|
/// - Parameter data: The data payload.
|
||||||
|
/// - Returns: OpenSSH data.
|
||||||
public func lengthAndData(of data: Data) -> Data {
|
public func lengthAndData(of data: Data) -> Data {
|
||||||
let rawLength = UInt32(data.count)
|
let rawLength = UInt32(data.count)
|
||||||
var endian = rawLength.bigEndian
|
var endian = rawLength.bigEndian
|
||||||
return Data(bytes: &endian, count: UInt32.bitWidth/8) + data
|
return Data(bytes: &endian, count: UInt32.bitWidth/8) + data
|
||||||
}
|
}
|
||||||
|
|
||||||
public func curveIdentifier(for algorithm: Algorithm, length: Int) -> String {
|
/// The fully qualified OpenSSH identifier for the algorithm.
|
||||||
switch algorithm {
|
/// - Parameters:
|
||||||
case .ellipticCurve:
|
/// - algorithm: The algorithm to identify.
|
||||||
return "nistp" + String(describing: length)
|
/// - length: The key length of the algorithm.
|
||||||
}
|
/// - Returns: The OpenSSH identifier for the algorithm.
|
||||||
}
|
|
||||||
|
|
||||||
public func curveType(for algorithm: Algorithm, length: Int) -> String {
|
public func curveType(for algorithm: Algorithm, length: Int) -> String {
|
||||||
switch algorithm {
|
switch algorithm {
|
||||||
case .ellipticCurve:
|
case .ellipticCurve:
|
||||||
return "ecdsa-sha2-nistp" + String(describing: length)
|
return "ecdsa-sha2-nistp" + String(describing: length)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The OpenSSH identifier for an algorithm.
|
||||||
|
/// - Parameters:
|
||||||
|
/// - algorithm: The algorithm to identify.
|
||||||
|
/// - length: The key length of the algorithm.
|
||||||
|
/// - Returns: The OpenSSH identifier for the algorithm.
|
||||||
|
private func curveIdentifier(for algorithm: Algorithm, length: Int) -> String {
|
||||||
|
switch algorithm {
|
||||||
|
case .ellipticCurve:
|
||||||
|
return "nistp" + String(describing: length)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,18 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
/// Reads OpenSSH protocol data.
|
||||||
public class OpenSSHReader {
|
public class OpenSSHReader {
|
||||||
|
|
||||||
var remaining: Data
|
var remaining: Data
|
||||||
|
|
||||||
|
/// Initialize the reader with an OpenSSH data payload.
|
||||||
|
/// - Parameter data: The data to read.
|
||||||
public init(data: Data) {
|
public init(data: Data) {
|
||||||
remaining = Data(data)
|
remaining = Data(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Reads the next chunk of data from the playload.
|
||||||
|
/// - Returns: The next chunk of data.
|
||||||
public func readNextChunk() -> Data {
|
public func readNextChunk() -> Data {
|
||||||
let lengthRange = 0..<(UInt32.bitWidth/8)
|
let lengthRange = 0..<(UInt32.bitWidth/8)
|
||||||
let lengthChunk = remaining[lengthRange]
|
let lengthChunk = remaining[lengthRange]
|
||||||
|
@ -1,25 +1,32 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import Combine
|
import Combine
|
||||||
|
|
||||||
|
/// A "Store Store," which holds a list of type-erased stores.
|
||||||
public class SecretStoreList: ObservableObject {
|
public class SecretStoreList: ObservableObject {
|
||||||
|
|
||||||
|
/// The Stores managed by the SecretStoreList.
|
||||||
@Published public var stores: [AnySecretStore] = []
|
@Published public var stores: [AnySecretStore] = []
|
||||||
|
/// A modifiable store, if one is available.
|
||||||
@Published public var modifiableStore: AnySecretStoreModifiable?
|
@Published public var modifiableStore: AnySecretStoreModifiable?
|
||||||
private var sinks: [AnyCancellable] = []
|
private var sinks: [AnyCancellable] = []
|
||||||
|
|
||||||
|
/// Initializes a SecretStoreList.
|
||||||
public init() {
|
public init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Adds a non-type-erased SecretStore to the list.
|
||||||
public func add<SecretStoreType: SecretStore>(store: SecretStoreType) {
|
public func add<SecretStoreType: SecretStore>(store: SecretStoreType) {
|
||||||
addInternal(store: AnySecretStore(store))
|
addInternal(store: AnySecretStore(store))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Adds a non-type-erased modifiable SecretStore.
|
||||||
public func add<SecretStoreType: SecretStoreModifiable>(store: SecretStoreType) {
|
public func add<SecretStoreType: SecretStoreModifiable>(store: SecretStoreType) {
|
||||||
let modifiable = AnySecretStoreModifiable(modifiable: store)
|
let modifiable = AnySecretStoreModifiable(modifiable: store)
|
||||||
modifiableStore = modifiable
|
modifiableStore = modifiable
|
||||||
addInternal(store: modifiable)
|
addInternal(store: modifiable)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A boolean describing whether there are any Stores available.
|
||||||
public var anyAvailable: Bool {
|
public var anyAvailable: Bool {
|
||||||
stores.reduce(false, { $0 || $1.isAvailable })
|
stores.reduce(false, { $0 || $1.isAvailable })
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,26 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
/// The base protocol for describing a Secret
|
||||||
public protocol Secret: Identifiable, Hashable {
|
public protocol Secret: Identifiable, Hashable {
|
||||||
|
|
||||||
|
/// A user-facing string identifying the Secret.
|
||||||
var name: String { get }
|
var name: String { get }
|
||||||
|
/// The algorithm this secret uses.
|
||||||
var algorithm: Algorithm { get }
|
var algorithm: Algorithm { get }
|
||||||
|
/// The key size for the secret.
|
||||||
var keySize: Int { get }
|
var keySize: Int { get }
|
||||||
|
/// The public key data for the secret.
|
||||||
var publicKey: Data { get }
|
var publicKey: Data { get }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The type of algorithm the Secret uses. Currently, only elliptic curve algorithms are supported.
|
||||||
public enum Algorithm: Hashable {
|
public enum Algorithm: Hashable {
|
||||||
|
|
||||||
case ellipticCurve
|
case ellipticCurve
|
||||||
|
|
||||||
|
/// Initializes the Algorithm with a secAttr representation of an algorithm.
|
||||||
|
/// - Parameter secAttr: the secAttr, represented as an NSNumber.
|
||||||
public init(secAttr: NSNumber) {
|
public init(secAttr: NSNumber) {
|
||||||
let secAttrString = secAttr.stringValue as CFString
|
let secAttrString = secAttr.stringValue as CFString
|
||||||
switch secAttrString {
|
switch secAttrString {
|
||||||
|
@ -1,25 +1,55 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import Combine
|
import Combine
|
||||||
|
|
||||||
|
/// Manages access to Secrets, and performs signature operations on data using those Secrets.
|
||||||
public protocol SecretStore: ObservableObject, Identifiable {
|
public protocol SecretStore: ObservableObject, Identifiable {
|
||||||
|
|
||||||
associatedtype SecretType: Secret
|
associatedtype SecretType: Secret
|
||||||
|
|
||||||
|
/// A boolean indicating whether or not the store is available.
|
||||||
var isAvailable: Bool { get }
|
var isAvailable: Bool { get }
|
||||||
|
/// A unique identifier for the store.
|
||||||
var id: UUID { get }
|
var id: UUID { get }
|
||||||
|
/// A user-facing name for the store.
|
||||||
var name: String { get }
|
var name: String { get }
|
||||||
|
/// The secrets the store manages.
|
||||||
var secrets: [SecretType] { get }
|
var secrets: [SecretType] { get }
|
||||||
|
|
||||||
|
/// Signs a data payload with a specified Secret.
|
||||||
|
/// - Parameters:
|
||||||
|
/// - data: The data to sign.
|
||||||
|
/// - secret: The ``Secret`` to sign with.
|
||||||
|
/// - provenance: A ``SigningRequestProvenance`` describing where the request came from.
|
||||||
|
/// - Returns: A ``SignedData`` object, containing the signature and metadata about the signature process.
|
||||||
func sign(data: Data, with secret: SecretType, for provenance: SigningRequestProvenance) throws -> SignedData
|
func sign(data: Data, with secret: SecretType, for provenance: SigningRequestProvenance) throws -> SignedData
|
||||||
|
|
||||||
|
/// Persists user authorization for access to a secret.
|
||||||
|
/// - Parameters:
|
||||||
|
/// - secret: The ``Secret`` to persist the authorization for.
|
||||||
|
/// - duration: The duration that the authorization should persist for.
|
||||||
|
/// - Note: This is used for temporarily unlocking access to a secret which would otherwise require authentication every single use. This is useful for situations where the user anticipates several rapid accesses to a authorization-guarded secret.
|
||||||
func persistAuthentication(secret: SecretType, forDuration duration: TimeInterval) throws
|
func persistAuthentication(secret: SecretType, forDuration duration: TimeInterval) throws
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A SecretStore that the Secretive admin app can modify.
|
||||||
public protocol SecretStoreModifiable: SecretStore {
|
public protocol SecretStoreModifiable: SecretStore {
|
||||||
|
|
||||||
|
/// Creates a new ``Secret`` in the store.
|
||||||
|
/// - Parameters:
|
||||||
|
/// - name: The user-facing name for the ``Secret``.
|
||||||
|
/// - requiresAuthentication: A boolean indicating whether or not the user will be required to authenticate before performing signature operations with the secret.
|
||||||
func create(name: String, requiresAuthentication: Bool) throws
|
func create(name: String, requiresAuthentication: Bool) throws
|
||||||
|
|
||||||
|
/// Deletes a Secret in the store.
|
||||||
|
/// - Parameters:
|
||||||
|
/// - secret: The ``Secret`` to delete.
|
||||||
func delete(secret: SecretType) throws
|
func delete(secret: SecretType) throws
|
||||||
|
|
||||||
|
/// Updates the name of a Secret in the store.
|
||||||
|
/// - Parameters:
|
||||||
|
/// - secret: The ``Secret`` to update.
|
||||||
|
/// - name: The new name for the Secret.
|
||||||
func update(secret: SecretType, name: String) throws
|
func update(secret: SecretType, name: String) throws
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,17 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
/// Describes the output of a sign request.
|
||||||
public struct SignedData {
|
public struct SignedData {
|
||||||
|
|
||||||
|
/// The signed data.
|
||||||
public let data: Data
|
public let data: Data
|
||||||
|
/// A boolean describing whether authentication was required during the signature process.
|
||||||
public let requiredAuthentication: Bool
|
public let requiredAuthentication: Bool
|
||||||
|
|
||||||
|
/// Initializes a new SignedData.
|
||||||
|
/// - Parameters:
|
||||||
|
/// - data: The signed data.
|
||||||
|
/// - requiredAuthentication: A boolean describing whether authentication was required during the signature process.
|
||||||
public init(data: Data, requiredAuthentication: Bool) {
|
public init(data: Data, requiredAuthentication: Bool) {
|
||||||
self.data = data
|
self.data = data
|
||||||
self.requiredAuthentication = requiredAuthentication
|
self.requiredAuthentication = requiredAuthentication
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import AppKit
|
import AppKit
|
||||||
|
|
||||||
|
/// Describes the chain of applications that requested a signature operation.
|
||||||
public struct SigningRequestProvenance: Equatable {
|
public struct SigningRequestProvenance: Equatable {
|
||||||
|
|
||||||
|
/// A list of processes involved in the request.
|
||||||
|
/// - Note: A chain will typically consist of many elements even for a simple request. For example, running `git fetch` in Terminal.app would generate a request chain of `ssh` -> `git` -> `zsh` -> `login` -> `Terminal.app`
|
||||||
public var chain: [Process]
|
public var chain: [Process]
|
||||||
public init(root: Process) {
|
public init(root: Process) {
|
||||||
self.chain = [root]
|
self.chain = [root]
|
||||||
@ -12,10 +15,12 @@ public struct SigningRequestProvenance: Equatable {
|
|||||||
|
|
||||||
extension SigningRequestProvenance {
|
extension SigningRequestProvenance {
|
||||||
|
|
||||||
|
/// The `Process` which initiated the signing request.
|
||||||
public var origin: Process {
|
public var origin: Process {
|
||||||
chain.last!
|
chain.last!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A boolean describing whether all processes in the request chain had a valid code signature.
|
||||||
public var intact: Bool {
|
public var intact: Bool {
|
||||||
chain.allSatisfy { $0.validSignature }
|
chain.allSatisfy { $0.validSignature }
|
||||||
}
|
}
|
||||||
@ -24,16 +29,33 @@ extension SigningRequestProvenance {
|
|||||||
|
|
||||||
extension SigningRequestProvenance {
|
extension SigningRequestProvenance {
|
||||||
|
|
||||||
|
/// Describes a process in a `SigningRequestProvenance` chain.
|
||||||
public struct Process: Equatable {
|
public struct Process: Equatable {
|
||||||
|
|
||||||
|
/// The pid of the process.
|
||||||
public let pid: Int32
|
public let pid: Int32
|
||||||
|
/// A user-facing name for the process.
|
||||||
public let processName: String
|
public let processName: String
|
||||||
|
/// A user-facing name for the application, if one exists.
|
||||||
public let appName: String?
|
public let appName: String?
|
||||||
|
/// An icon representation of the application, if one exists.
|
||||||
public let iconURL: URL?
|
public let iconURL: URL?
|
||||||
|
/// The path the process exists at.
|
||||||
public let path: String
|
public let path: String
|
||||||
|
/// A boolean describing whether or not the process has a valid code signature.
|
||||||
public let validSignature: Bool
|
public let validSignature: Bool
|
||||||
|
/// The pid of the process's parent.
|
||||||
public let parentPID: Int32?
|
public let parentPID: Int32?
|
||||||
|
|
||||||
|
/// Initializes a Process.
|
||||||
|
/// - Parameters:
|
||||||
|
/// - pid: The pid of the process.
|
||||||
|
/// - processName: A user-facing name for the process.
|
||||||
|
/// - appName: A user-facing name for the application, if one exists.
|
||||||
|
/// - iconURL: An icon representation of the application, if one exists.
|
||||||
|
/// - path: The path the process exists at.
|
||||||
|
/// - validSignature: A boolean describing whether or not the process has a valid code signature.
|
||||||
|
/// - parentPID: The pid of the process's parent.
|
||||||
public init(pid: Int32, processName: String, appName: String?, iconURL: URL?, path: String, validSignature: Bool, parentPID: Int32?) {
|
public init(pid: Int32, processName: String, appName: String?, iconURL: URL?, path: String, validSignature: Bool, parentPID: Int32?) {
|
||||||
self.pid = pid
|
self.pid = pid
|
||||||
self.processName = processName
|
self.processName = processName
|
||||||
@ -44,6 +66,7 @@ extension SigningRequestProvenance {
|
|||||||
self.parentPID = parentPID
|
self.parentPID = parentPID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The best user-facing name to display for the process.
|
||||||
public var displayName: String {
|
public var displayName: String {
|
||||||
appName ?? processName
|
appName ?? processName
|
||||||
}
|
}
|
||||||
|
@ -97,7 +97,6 @@ extension SecureEnclave {
|
|||||||
}
|
}
|
||||||
reloadSecrets()
|
reloadSecrets()
|
||||||
}
|
}
|
||||||
|
|
||||||
public func sign(data: Data, with secret: SecretType, for provenance: SigningRequestProvenance) throws -> SignedData {
|
public func sign(data: Data, with secret: SecretType, for provenance: SigningRequestProvenance) throws -> SignedData {
|
||||||
let context: LAContext
|
let context: LAContext
|
||||||
if let existing = persistedAuthenticationContexts[secret], existing.valid {
|
if let existing = persistedAuthenticationContexts[secret], existing.valid {
|
||||||
@ -141,6 +140,10 @@ extension SecureEnclave {
|
|||||||
return SignedData(data: signature as Data, requiredAuthentication: requiredAuthentication)
|
return SignedData(data: signature as Data, requiredAuthentication: requiredAuthentication)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <#Description#>
|
||||||
|
/// - Parameters:
|
||||||
|
/// - secret: <#secret description#>
|
||||||
|
/// - duration: <#duration description#>
|
||||||
public func persistAuthentication(secret: Secret, forDuration duration: TimeInterval) throws {
|
public func persistAuthentication(secret: Secret, forDuration duration: TimeInterval) throws {
|
||||||
let newContext = LAContext()
|
let newContext = LAContext()
|
||||||
newContext.touchIDAuthenticationAllowableReuseDuration = duration
|
newContext.touchIDAuthenticationAllowableReuseDuration = duration
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
2C4A9D2F2636FFD3008CC8E2 /* RenameSecretView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C4A9D2E2636FFD3008CC8E2 /* RenameSecretView.swift */; };
|
2C4A9D2F2636FFD3008CC8E2 /* RenameSecretView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C4A9D2E2636FFD3008CC8E2 /* RenameSecretView.swift */; };
|
||||||
50020BB024064869003D4025 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50020BAF24064869003D4025 /* AppDelegate.swift */; };
|
50020BB024064869003D4025 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50020BAF24064869003D4025 /* AppDelegate.swift */; };
|
||||||
|
50033AC327813F1700253856 /* BundleIDs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50033AC227813F1700253856 /* BundleIDs.swift */; };
|
||||||
5003EF3B278005E800DF2006 /* SecretKit in Frameworks */ = {isa = PBXBuildFile; productRef = 5003EF3A278005E800DF2006 /* SecretKit */; };
|
5003EF3B278005E800DF2006 /* SecretKit in Frameworks */ = {isa = PBXBuildFile; productRef = 5003EF3A278005E800DF2006 /* SecretKit */; };
|
||||||
5003EF3D278005F300DF2006 /* Brief in Frameworks */ = {isa = PBXBuildFile; productRef = 5003EF3C278005F300DF2006 /* Brief */; };
|
5003EF3D278005F300DF2006 /* Brief in Frameworks */ = {isa = PBXBuildFile; productRef = 5003EF3C278005F300DF2006 /* Brief */; };
|
||||||
5003EF3F278005F300DF2006 /* SecretAgentKit in Frameworks */ = {isa = PBXBuildFile; productRef = 5003EF3E278005F300DF2006 /* SecretAgentKit */; };
|
5003EF3F278005F300DF2006 /* SecretAgentKit in Frameworks */ = {isa = PBXBuildFile; productRef = 5003EF3E278005F300DF2006 /* SecretAgentKit */; };
|
||||||
@ -104,6 +105,7 @@
|
|||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
2C4A9D2E2636FFD3008CC8E2 /* RenameSecretView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RenameSecretView.swift; sourceTree = "<group>"; };
|
2C4A9D2E2636FFD3008CC8E2 /* RenameSecretView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RenameSecretView.swift; sourceTree = "<group>"; };
|
||||||
50020BAF24064869003D4025 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
50020BAF24064869003D4025 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||||
|
50033AC227813F1700253856 /* BundleIDs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BundleIDs.swift; sourceTree = "<group>"; };
|
||||||
5003EF39278005C800DF2006 /* Packages */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = Packages; sourceTree = "<group>"; };
|
5003EF39278005C800DF2006 /* Packages */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = Packages; sourceTree = "<group>"; };
|
||||||
50153E1F250AFCB200525160 /* UpdateView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateView.swift; sourceTree = "<group>"; };
|
50153E1F250AFCB200525160 /* UpdateView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateView.swift; sourceTree = "<group>"; };
|
||||||
50153E21250DECA300525160 /* SecretListItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecretListItemView.swift; sourceTree = "<group>"; };
|
50153E21250DECA300525160 /* SecretListItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecretListItemView.swift; sourceTree = "<group>"; };
|
||||||
@ -181,6 +183,14 @@
|
|||||||
/* End PBXFrameworksBuildPhase section */
|
/* End PBXFrameworksBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXGroup section */
|
/* Begin PBXGroup section */
|
||||||
|
50033AC427813F1C00253856 /* Helpers */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
50033AC227813F1700253856 /* BundleIDs.swift */,
|
||||||
|
);
|
||||||
|
path = Helpers;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
50617D7623FCE48D0099B055 = {
|
50617D7623FCE48D0099B055 = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@ -210,6 +220,7 @@
|
|||||||
50617D8223FCE48E0099B055 /* App.swift */,
|
50617D8223FCE48E0099B055 /* App.swift */,
|
||||||
508A58B0241ED1C40069DC07 /* Views */,
|
508A58B0241ED1C40069DC07 /* Views */,
|
||||||
508A58B1241ED1EA0069DC07 /* Controllers */,
|
508A58B1241ED1EA0069DC07 /* Controllers */,
|
||||||
|
50033AC427813F1C00253856 /* Helpers */,
|
||||||
50617D8623FCE48E0099B055 /* Assets.xcassets */,
|
50617D8623FCE48E0099B055 /* Assets.xcassets */,
|
||||||
50617D8E23FCE48E0099B055 /* Info.plist */,
|
50617D8E23FCE48E0099B055 /* Info.plist */,
|
||||||
508BF28D25B4F005009EFB7E /* InternetAccessPolicy.plist */,
|
508BF28D25B4F005009EFB7E /* InternetAccessPolicy.plist */,
|
||||||
@ -469,6 +480,7 @@
|
|||||||
5079BA0F250F29BF00EA86F4 /* StoreListView.swift in Sources */,
|
5079BA0F250F29BF00EA86F4 /* StoreListView.swift in Sources */,
|
||||||
50617DD223FCEFA90099B055 /* PreviewStore.swift in Sources */,
|
50617DD223FCEFA90099B055 /* PreviewStore.swift in Sources */,
|
||||||
5066A6F7251829B1004B5A36 /* ShellConfigurationController.swift in Sources */,
|
5066A6F7251829B1004B5A36 /* ShellConfigurationController.swift in Sources */,
|
||||||
|
50033AC327813F1700253856 /* BundleIDs.swift in Sources */,
|
||||||
508A58B3241ED2180069DC07 /* AgentStatusChecker.swift in Sources */,
|
508A58B3241ED2180069DC07 /* AgentStatusChecker.swift in Sources */,
|
||||||
50C385A52407A76D00AF2719 /* SecretDetailView.swift in Sources */,
|
50C385A52407A76D00AF2719 /* SecretDetailView.swift in Sources */,
|
||||||
5099A02423FD2AAA0062B6F2 /* CreateSecretView.swift in Sources */,
|
5099A02423FD2AAA0062B6F2 /* CreateSecretView.swift in Sources */,
|
||||||
|
Loading…
Reference in New Issue
Block a user