mirror of
				https://github.com/maxgoedjen/secretive.git
				synced 2025-11-03 17:00:56 +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