mirror of
https://github.com/maxgoedjen/secretive.git
synced 2026-04-09 18:57:22 +02:00
Compare commits
40 Commits
xcode_14
...
automatic_
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
61b245c7b0 | ||
|
|
6a4d96ac02 | ||
|
|
b2a8928cbb | ||
|
|
93114a00de | ||
|
|
5d38da1f4b | ||
|
|
c917d6067a | ||
|
|
7c8d488ef3 | ||
|
|
d3e3147cc1 | ||
|
|
7715ab4d2b | ||
|
|
b78aa7f0ec | ||
|
|
34703060b8 | ||
|
|
382913cb99 | ||
|
|
20cbaac6f6 | ||
|
|
47d736cb0d | ||
|
|
fa0e81cd8e | ||
|
|
e31db0f4fa | ||
|
|
6dd4088d2f | ||
|
|
53087c0439 | ||
|
|
f32a3a0abd | ||
|
|
8e3d53b5c9 | ||
|
|
29a9ae9dc9 | ||
|
|
22832b474f | ||
|
|
38f48e74b7 | ||
|
|
17f5797552 | ||
|
|
8255cbb0e9 | ||
|
|
ab9f8b5600 | ||
|
|
e97601ad9c | ||
|
|
200cd6ea9d | ||
|
|
2bd8b008ca | ||
|
|
23611877ca | ||
|
|
32ebb7f6ec | ||
|
|
0b2b9f8a13 | ||
|
|
71280fd7a5 | ||
|
|
6c6364f92c | ||
|
|
403709ac83 | ||
|
|
5f055efa18 | ||
|
|
e77812c06c | ||
|
|
8744313ba1 | ||
|
|
26d6ced9ee | ||
|
|
71b4780488 |
19
.github/scripts/signing.sh
vendored
19
.github/scripts/signing.sh
vendored
@@ -1,22 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Import certificate and private key
|
||||
echo $SIGNING_DATA | base64 -d -o Signing.p12
|
||||
security create-keychain -p ci ci.keychain
|
||||
security default-keychain -s ci.keychain
|
||||
security list-keychains -s ci.keychain
|
||||
security import ./Signing.p12 -k ci.keychain -P $SIGNING_PASSWORD -A
|
||||
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k ci ci.keychain
|
||||
|
||||
# Import Profiles
|
||||
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
|
||||
echo $HOST_PROFILE_DATA | base64 -d -o Host.provisionprofile
|
||||
HOST_UUID=`grep UUID -A1 -a Host.provisionprofile | grep -io "[-A-F0-9]\{36\}"`
|
||||
cp Host.provisionprofile ~/Library/MobileDevice/Provisioning\ Profiles/$HOST_UUID.provisionprofile
|
||||
echo $AGENT_PROFILE_DATA | base64 -d -o Agent.provisionprofile
|
||||
AGENT_UUID=`grep UUID -A1 -a Agent.provisionprofile | grep -io "[-A-F0-9]\{36\}"`
|
||||
cp Agent.provisionprofile ~/Library/MobileDevice/Provisioning\ Profiles/$AGENT_UUID.provisionprofile
|
||||
|
||||
# Create directories for ASC key
|
||||
mkdir ~/.private_keys
|
||||
echo -n "$APPLE_API_KEY_DATA" > ~/.private_keys/AuthKey_$APPLE_API_KEY_ID.p8
|
||||
echo -n "$APPLE_API_KEY_DATA" > ~/.private_keys/AuthKey.p8
|
||||
|
||||
16
.github/workflows/add-to-project.yml
vendored
Normal file
16
.github/workflows/add-to-project.yml
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
name: Add bugs to bugs project
|
||||
|
||||
on:
|
||||
issues:
|
||||
types:
|
||||
- opened
|
||||
|
||||
jobs:
|
||||
add-to-project:
|
||||
name: Add issue to project
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/add-to-project@v0.0.3
|
||||
with:
|
||||
project-url: https://github.com/users/maxgoedjen/projects/1
|
||||
github-token: ${{ secrets.ADD_TO_PROJECT_PAT }}
|
||||
10
.github/workflows/nightly.yml
vendored
10
.github/workflows/nightly.yml
vendored
@@ -5,7 +5,7 @@ on:
|
||||
- cron: "0 8 * * *"
|
||||
jobs:
|
||||
build:
|
||||
runs-on: macos-11.0
|
||||
runs-on: macOS-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
@@ -19,7 +19,7 @@ jobs:
|
||||
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
|
||||
run: ./.github/scripts/signing.sh
|
||||
- name: Set Environment
|
||||
run: sudo xcrun xcode-select -s /Applications/Xcode_13.2.1.app
|
||||
run: sudo xcrun xcode-select -s /Applications/Xcode_14.1.app
|
||||
- name: Update Build Number
|
||||
env:
|
||||
RUN_ID: ${{ github.run_id }}
|
||||
@@ -40,10 +40,14 @@ jobs:
|
||||
run: xcrun notarytool submit --key ~/.private_keys/AuthKey_$APPLE_API_KEY_ID.p8 --key-id $APPLE_API_KEY_ID --issuer $APPLE_API_ISSUER Secretive.zip
|
||||
- name: Document SHAs
|
||||
run: |
|
||||
echo "sha-512:"
|
||||
shasum -a 512 Secretive.zip
|
||||
shasum -a 512 Archive.zip
|
||||
echo "sha-256:"
|
||||
shasum -a 256 Secretive.zip
|
||||
shasum -a 256 Archive.zip
|
||||
- name: Upload App to Artifacts
|
||||
uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: Secretive.zip
|
||||
path: Secretive.zip
|
||||
path: Secretive.zip
|
||||
|
||||
67
.github/workflows/release.yml
vendored
67
.github/workflows/release.yml
vendored
@@ -5,32 +5,33 @@ on:
|
||||
tags:
|
||||
- '*'
|
||||
jobs:
|
||||
test:
|
||||
runs-on: macos-11.0
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Setup Signing
|
||||
env:
|
||||
SIGNING_DATA: ${{ secrets.SIGNING_DATA }}
|
||||
SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }}
|
||||
HOST_PROFILE_DATA: ${{ secrets.HOST_PROFILE_DATA }}
|
||||
AGENT_PROFILE_DATA: ${{ secrets.AGENT_PROFILE_DATA }}
|
||||
APPLE_API_KEY_DATA: ${{ secrets.APPLE_API_KEY_DATA }}
|
||||
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
|
||||
run: ./.github/scripts/signing.sh
|
||||
- name: Set Environment
|
||||
run: sudo xcrun xcode-select -s /Applications/Xcode_13.2.1.app
|
||||
- name: Test
|
||||
run: |
|
||||
pushd Sources/Packages
|
||||
swift test
|
||||
popd
|
||||
# test:
|
||||
# runs-on: macOS-latest
|
||||
# timeout-minutes: 10
|
||||
# steps:
|
||||
# - uses: actions/checkout@v3
|
||||
# - name: Setup Signing
|
||||
# env:
|
||||
# SIGNING_DATA: ${{ secrets.SIGNING_DATA }}
|
||||
# SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }}
|
||||
# HOST_PROFILE_DATA: ${{ secrets.HOST_PROFILE_DATA }}
|
||||
# AGENT_PROFILE_DATA: ${{ secrets.AGENT_PROFILE_DATA }}
|
||||
# APPLE_API_KEY_DATA: ${{ secrets.APPLE_API_KEY_DATA }}
|
||||
# APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
|
||||
# run: ./.github/scripts/signing.sh
|
||||
# - name: Set Environment
|
||||
# run: sudo xcrun xcode-select -s /Applications/Xcode_14.1.app
|
||||
# - name: Test
|
||||
# run: |
|
||||
# pushd Sources/Packages
|
||||
# swift test
|
||||
# popd
|
||||
|
||||
build:
|
||||
runs-on: macos-11.0
|
||||
runs-on: macOS-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- name: Setup Signing
|
||||
env:
|
||||
SIGNING_DATA: ${{ secrets.SIGNING_DATA }}
|
||||
@@ -41,7 +42,7 @@ jobs:
|
||||
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
|
||||
run: ./.github/scripts/signing.sh
|
||||
- name: Set Environment
|
||||
run: sudo xcrun xcode-select -s /Applications/Xcode_13.2.1.app
|
||||
run: sudo xcrun xcode-select -s /Applications/Xcode_14.1.app
|
||||
- name: Update Build Number
|
||||
env:
|
||||
TAG_NAME: ${{ github.ref }}
|
||||
@@ -52,20 +53,32 @@ jobs:
|
||||
sed -i '' -e "s/GITHUB_BUILD_NUMBER/1.$RUN_ID/g" Sources/Config/Config.xcconfig
|
||||
sed -i '' -e "s/GITHUB_BUILD_URL/https:\/\/github.com\/maxgoedjen\/secretive\/actions\/runs\/$RUN_ID/g" Sources/Secretive/Credits.rtf
|
||||
- name: Build
|
||||
run: xcrun xcodebuild -project Sources/Secretive.xcodeproj -scheme Secretive -configuration Release -archivePath Archive.xcarchive archive
|
||||
env:
|
||||
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
|
||||
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
|
||||
run: xcrun xcodebuild -project Sources/Secretive.xcodeproj -scheme Secretive -configuration Release -archivePath Archive.xcarchive -destination "generic/platform=macOS" archive
|
||||
- name: Export Products
|
||||
env:
|
||||
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
|
||||
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
|
||||
run: xcrun xcodebuild -exportArchive -archivePath Archive.xcarchive -exportPath Export -exportOptionsPlist Sources/Config/ExportOptions.plist -allowProvisioningUpdates -authenticationKeyIssuerID $APPLE_API_ISSUER -authenticationKeyID $APPLE_API_KEY_ID -authenticationKeyPath ~/.private_keys/AuthKey.p8
|
||||
- name: Create ZIPs
|
||||
run: |
|
||||
ditto -c -k --sequesterRsrc --keepParent Archive.xcarchive/Products/Applications/Secretive.app ./Secretive.zip
|
||||
ditto -c -k --sequesterRsrc --keepParent Export/Secretive.app ./Secretive.zip
|
||||
ditto -c -k --sequesterRsrc --keepParent Archive.xcarchive ./Archive.zip
|
||||
- name: Notarize
|
||||
env:
|
||||
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
|
||||
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
|
||||
run: xcrun notarytool submit --key ~/.private_keys/AuthKey_$APPLE_API_KEY_ID.p8 --key-id $APPLE_API_KEY_ID --issuer $APPLE_API_ISSUER Secretive.zip
|
||||
run: xcrun notarytool submit --key $(pwd)/.private_keys/AuthKey_$APPLE_API_KEY_ID.p8 --key-id $APPLE_API_KEY_ID --issuer $APPLE_API_ISSUER Secretive.zip
|
||||
- name: Document SHAs
|
||||
run: |
|
||||
echo "sha-512:"
|
||||
shasum -a 512 Secretive.zip
|
||||
shasum -a 512 Archive.zip
|
||||
echo "sha-256:"
|
||||
shasum -a 256 Secretive.zip
|
||||
shasum -a 256 Archive.zip
|
||||
- name: Create Release
|
||||
id: create_release
|
||||
uses: actions/create-release@v1
|
||||
|
||||
4
.github/workflows/test.yml
vendored
4
.github/workflows/test.yml
vendored
@@ -3,12 +3,12 @@ name: Test
|
||||
on: [push, pull_request]
|
||||
jobs:
|
||||
test:
|
||||
runs-on: macos-11.0
|
||||
runs-on: macOS-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set Environment
|
||||
run: sudo xcrun xcode-select -s /Applications/Xcode_13.2.1.app
|
||||
run: sudo xcrun xcode-select -s /Applications/Xcode_14.1.app
|
||||
- name: Test
|
||||
run: |
|
||||
pushd Sources/Packages
|
||||
|
||||
@@ -26,6 +26,15 @@ Host *
|
||||
IdentityAgent /Users/$YOUR_USERNAME/Library/Containers/com.maxgoedjen.Secretive.SecretAgent/Data/socket.ssh
|
||||
```
|
||||
|
||||
## nushell
|
||||
|
||||
Add this to your `~/.ssh/config` (the path should match the socket path from the setup flow).
|
||||
|
||||
```
|
||||
Host *
|
||||
IdentityAgent /Users/$YOUR_USERNAME/Library/Containers/com.maxgoedjen.Secretive.SecretAgent/Data/socket.ssh
|
||||
```
|
||||
|
||||
## Cyberduck
|
||||
|
||||
Add this to `~/Library/LaunchAgents/com.maxgoedjen.Secretive.SecretAgent.plist`
|
||||
@@ -51,6 +60,31 @@ Add this to `~/Library/LaunchAgents/com.maxgoedjen.Secretive.SecretAgent.plist`
|
||||
|
||||
Log out and log in again before launching Cyberduck.
|
||||
|
||||
## Mountain Duck
|
||||
|
||||
Add this to `~/Library/LaunchAgents/com.maxgoedjen.Secretive.SecretAgent.plist`
|
||||
|
||||
```
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string>link-ssh-auth-sock</string>
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>/bin/sh</string>
|
||||
<string>-c</string>
|
||||
<string>/bin/ln -sf $HOME/Library/Containers/com.maxgoedjen.Secretive.SecretAgent/Data/socket.ssh $SSH_AUTH_SOCK</string>
|
||||
</array>
|
||||
<key>RunAtLoad</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
```
|
||||
|
||||
Log out and log in again before launching Mountain Duck.
|
||||
|
||||
## GitKraken
|
||||
|
||||
Add this to `~/Library/LaunchAgents/com.maxgoedjen.Secretive.SecretAgent.plist`
|
||||
|
||||
4
FAQ.md
4
FAQ.md
@@ -12,6 +12,10 @@ Secretive relies on the `SSH_AUTH_SOCK` environment variable being respected. Th
|
||||
|
||||
Please run `ssh -Tv git@github.com` in your terminal and paste the output in a [new GitHub issue](https://github.com/maxgoedjen/secretive/issues/new) with a description of your issue.
|
||||
|
||||
### Secretive was working for me, but now it has stopped
|
||||
|
||||
Try running the "Setup Secretive" process by clicking on "Help", then "Setup Secretive." If that doesn't work, follow the process above.
|
||||
|
||||
### Secretive prompts me to type my password instead of using my Apple Watch
|
||||
|
||||
1) Make sure you have enabled "Use your Apple Watch to unlock apps and your Mac" in System Preferences --> Security & Privacy:
|
||||
|
||||
10
Sources/Config/ExportOptions.plist
Normal file
10
Sources/Config/ExportOptions.plist
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>method</key>
|
||||
<string>developer-id</string>
|
||||
<key>teamID</key>
|
||||
<string>Z72PRUAWF6</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -4,6 +4,25 @@ import OSLog
|
||||
import SecretKit
|
||||
import AppKit
|
||||
|
||||
enum OpenSSHCertificateError: Error {
|
||||
case unsupportedType
|
||||
case parsingFailed
|
||||
case doesNotExist
|
||||
}
|
||||
|
||||
extension OpenSSHCertificateError: CustomStringConvertible {
|
||||
public var description: String {
|
||||
switch self {
|
||||
case .unsupportedType:
|
||||
return "The key type was unsupported"
|
||||
case .parsingFailed:
|
||||
return "Failed to properly parse the SSH certificate"
|
||||
case .doesNotExist:
|
||||
return "Certificate does not exist"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The `Agent` is an implementation of an SSH agent. It manages coordination and access between a socket, traces requests, notifies witnesses and passes requests to stores.
|
||||
public class Agent {
|
||||
|
||||
@@ -11,6 +30,7 @@ public class Agent {
|
||||
private let witness: SigningWitness?
|
||||
private let writer = OpenSSHKeyWriter()
|
||||
private let requestTracer = SigningRequestTracer()
|
||||
private let certsPath = (NSHomeDirectory() as NSString).appendingPathComponent("PublicKeys") as String
|
||||
|
||||
/// Initializes an agent with a store list and a witness.
|
||||
/// - Parameters:
|
||||
@@ -83,12 +103,22 @@ extension Agent {
|
||||
var count = UInt32(secrets.count).bigEndian
|
||||
let countData = Data(bytes: &count, count: UInt32.bitWidth/8)
|
||||
var keyData = Data()
|
||||
let writer = OpenSSHKeyWriter()
|
||||
|
||||
for secret in secrets {
|
||||
let keyBlob = writer.data(secret: secret)
|
||||
let keyBlob: Data
|
||||
let curveData: Data
|
||||
|
||||
if let (certBlob, certName) = try? checkForCert(secret: secret) {
|
||||
keyBlob = certBlob
|
||||
curveData = certName
|
||||
} else {
|
||||
keyBlob = writer.data(secret: secret)
|
||||
curveData = writer.curveType(for: secret.algorithm, length: secret.keySize).data(using: .utf8)!
|
||||
}
|
||||
|
||||
keyData.append(writer.lengthAndData(of: keyBlob))
|
||||
let curveData = writer.curveType(for: secret.algorithm, length: secret.keySize).data(using: .utf8)!
|
||||
keyData.append(writer.lengthAndData(of: curveData))
|
||||
|
||||
}
|
||||
Logger().debug("Agent enumerated \(secrets.count) identities")
|
||||
return countData + keyData
|
||||
@@ -101,7 +131,13 @@ extension Agent {
|
||||
/// - Returns: An OpenSSH formatted Data payload containing the signed data response.
|
||||
func sign(data: Data, provenance: SigningRequestProvenance) throws -> Data {
|
||||
let reader = OpenSSHReader(data: data)
|
||||
let hash = reader.readNextChunk()
|
||||
var hash = reader.readNextChunk()
|
||||
|
||||
// Check if hash is actually an openssh certificate and reconstruct the public key if it is
|
||||
if let certPublicKey = try? getPublicKeyFromCert(certBlob: hash) {
|
||||
hash = certPublicKey
|
||||
}
|
||||
|
||||
guard let (store, secret) = secret(matching: hash) else {
|
||||
Logger().debug("Agent did not have a key matching \(hash as NSData)")
|
||||
throw AgentError.noMatchingKey
|
||||
@@ -161,6 +197,74 @@ extension Agent {
|
||||
|
||||
return signedData
|
||||
}
|
||||
|
||||
/// Reconstructs a public key from a ``Data`` object that contains an OpenSSH certificate. Currently only ecdsa certificates are supported
|
||||
/// - Parameter certBlock: The openssh certificate to extract the public key from
|
||||
/// - Returns: A ``Data`` object containing the public key in OpenSSH wire format
|
||||
func getPublicKeyFromCert(certBlob: Data) throws -> Data {
|
||||
let reader = OpenSSHReader(data: certBlob)
|
||||
let certType = String(decoding: reader.readNextChunk(), as: UTF8.self)
|
||||
|
||||
switch certType {
|
||||
case "ecdsa-sha2-nistp256-cert-v01@openssh.com",
|
||||
"ecdsa-sha2-nistp384-cert-v01@openssh.com",
|
||||
"ecdsa-sha2-nistp521-cert-v01@openssh.com":
|
||||
|
||||
_ = reader.readNextChunk() // nonce
|
||||
let curveIdentifier = reader.readNextChunk()
|
||||
let publicKey = reader.readNextChunk()
|
||||
|
||||
if let curveType = certType.replacingOccurrences(of: "-cert-v01@openssh.com", with: "").data(using: .utf8) {
|
||||
return writer.lengthAndData(of: curveType) +
|
||||
writer.lengthAndData(of: curveIdentifier) +
|
||||
writer.lengthAndData(of: publicKey)
|
||||
} else {
|
||||
throw OpenSSHCertificateError.parsingFailed
|
||||
}
|
||||
default:
|
||||
throw OpenSSHCertificateError.unsupportedType
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Attempts to find an OpenSSH Certificate that corresponds to a ``Secret``
|
||||
/// - Parameter secret: The secret to search for a certificate with
|
||||
/// - Returns: Two ``Data`` objects containing the certificate and certificate name respectively
|
||||
func checkForCert(secret: AnySecret) throws -> (Data, Data) {
|
||||
let minimalHex = writer.openSSHMD5Fingerprint(secret: secret).replacingOccurrences(of: ":", with: "")
|
||||
let certificatePath = certsPath.appending("/").appending("\(minimalHex)-cert.pub")
|
||||
|
||||
if FileManager.default.fileExists(atPath: certificatePath) {
|
||||
Logger().debug("Found certificate for \(secret.name)")
|
||||
do {
|
||||
let certContent = try String(contentsOfFile:certificatePath, encoding: .utf8)
|
||||
let certElements = certContent.trimmingCharacters(in: .whitespacesAndNewlines).components(separatedBy: " ")
|
||||
|
||||
if certElements.count >= 2 {
|
||||
if let certDecoded = Data(base64Encoded: certElements[1] as String) {
|
||||
if certElements.count >= 3 {
|
||||
if let certName = certElements[2].data(using: .utf8) {
|
||||
return (certDecoded, certName)
|
||||
} else if let certName = secret.name.data(using: .utf8) {
|
||||
Logger().info("Certificate for \(secret.name) does not have a name tag, using secret name instead")
|
||||
return (certDecoded, certName)
|
||||
} else {
|
||||
throw OpenSSHCertificateError.parsingFailed
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Logger().warning("Certificate found for \(secret.name) but failed to decode base64 key")
|
||||
throw OpenSSHCertificateError.parsingFailed
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
Logger().warning("Certificate found for \(secret.name) but failed to load")
|
||||
throw OpenSSHCertificateError.parsingFailed
|
||||
}
|
||||
}
|
||||
|
||||
throw OpenSSHCertificateError.doesNotExist
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,10 @@ extension SigningRequestTracer {
|
||||
func process(from pid: Int32) -> SigningRequestProvenance.Process {
|
||||
var pidAndNameInfo = self.pidAndNameInfo(from: pid)
|
||||
let ppid = pidAndNameInfo.kp_eproc.e_ppid != 0 ? pidAndNameInfo.kp_eproc.e_ppid : nil
|
||||
let procName = String(cString: &pidAndNameInfo.kp_proc.p_comm.0)
|
||||
let procName = withUnsafeMutablePointer(to: &pidAndNameInfo.kp_proc.p_comm.0) { pointer in
|
||||
String(cString: pointer)
|
||||
}
|
||||
|
||||
let pathPointer = UnsafeMutablePointer<UInt8>.allocate(capacity: Int(MAXPATHLEN))
|
||||
_ = proc_pidpath(pid, pathPointer, UInt32(MAXPATHLEN))
|
||||
let path = String(cString: pathPointer)
|
||||
|
||||
@@ -15,15 +15,21 @@ public class PublicKeyFileStoreController {
|
||||
|
||||
/// Writes out the keys specified to disk.
|
||||
/// - Parameter secrets: The Secrets to generate keys for.
|
||||
/// - Parameter clear: Whether or not the directory should be erased before writing keys.
|
||||
/// - Parameter clear: Whether or not any untracked files in the directory should be removed.
|
||||
public func generatePublicKeys(for secrets: [AnySecret], clear: Bool = false) throws {
|
||||
logger.log("Writing public keys to disk")
|
||||
if clear {
|
||||
try? FileManager.default.removeItem(at: URL(fileURLWithPath: directory))
|
||||
let validPaths = Set(secrets.map { publicKeyPath(for: $0) }).union(Set(secrets.map { sshCertificatePath(for: $0) }))
|
||||
let untracked = Set(try FileManager.default.contentsOfDirectory(atPath: directory)
|
||||
.map { "\(directory)/\($0)" })
|
||||
.subtracting(validPaths)
|
||||
for path in untracked {
|
||||
try? FileManager.default.removeItem(at: URL(fileURLWithPath: path))
|
||||
}
|
||||
}
|
||||
try? FileManager.default.createDirectory(at: URL(fileURLWithPath: directory), withIntermediateDirectories: false, attributes: nil)
|
||||
for secret in secrets {
|
||||
let path = path(for: secret)
|
||||
let path = publicKeyPath(for: secret)
|
||||
guard let data = keyWriter.openSSHString(secret: secret).data(using: .utf8) else { continue }
|
||||
FileManager.default.createFile(atPath: path, contents: data, attributes: nil)
|
||||
}
|
||||
@@ -34,9 +40,18 @@ public class PublicKeyFileStoreController {
|
||||
/// - Parameter secret: The Secret to return the path for.
|
||||
/// - Returns: The path to the Secret's public key.
|
||||
/// - Warning: This method returning a path does not imply that a key has been written to disk already. This method only describes where it will be written to.
|
||||
public func path<SecretType: Secret>(for secret: SecretType) -> String {
|
||||
public func publicKeyPath<SecretType: Secret>(for secret: SecretType) -> String {
|
||||
let minimalHex = keyWriter.openSSHMD5Fingerprint(secret: secret).replacingOccurrences(of: ":", with: "")
|
||||
return directory.appending("/").appending("\(minimalHex).pub")
|
||||
}
|
||||
|
||||
/// The path for a Secret's SSH Certificate public key.
|
||||
/// - Parameter secret: The Secret to return the path for.
|
||||
/// - Returns: The path to the SSH Certificate public key.
|
||||
/// - Warning: This method returning a path does not imply that a key has a SSH certificates. This method only describes where it will be.
|
||||
public func sshCertificatePath<SecretType: Secret>(for secret: SecretType) -> String {
|
||||
let minimalHex = keyWriter.openSSHMD5Fingerprint(secret: secret).replacingOccurrences(of: ":", with: "")
|
||||
return directory.appending("/").appending("\(minimalHex)-cert.pub")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -154,7 +154,7 @@ class NotificationDelegate: NSObject, UNUserNotificationCenterDelegate {
|
||||
case Notifier.Constants.persistAuthenticationCategoryIdentitifier:
|
||||
handlePersistAuthenticationResponse(response: response)
|
||||
default:
|
||||
fatalError()
|
||||
break
|
||||
}
|
||||
|
||||
completionHandler()
|
||||
|
||||
@@ -50,7 +50,6 @@
|
||||
50B8550D24138C4F009958AC /* DeleteSecretView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50B8550C24138C4F009958AC /* DeleteSecretView.swift */; };
|
||||
50BB046B2418AAAE00D6E079 /* EmptyStoreView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50BB046A2418AAAE00D6E079 /* EmptyStoreView.swift */; };
|
||||
50C385A52407A76D00AF2719 /* SecretDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50C385A42407A76D00AF2719 /* SecretDetailView.swift */; };
|
||||
50C511B0285064DB00704B27 /* MainActorWrappers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50C511AF285064DB00704B27 /* MainActorWrappers.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@@ -148,7 +147,6 @@
|
||||
50B8550C24138C4F009958AC /* DeleteSecretView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeleteSecretView.swift; sourceTree = "<group>"; };
|
||||
50BB046A2418AAAE00D6E079 /* EmptyStoreView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyStoreView.swift; sourceTree = "<group>"; };
|
||||
50C385A42407A76D00AF2719 /* SecretDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecretDetailView.swift; sourceTree = "<group>"; };
|
||||
50C511AF285064DB00704B27 /* MainActorWrappers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainActorWrappers.swift; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@@ -189,7 +187,6 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
50033AC227813F1700253856 /* BundleIDs.swift */,
|
||||
50C511AF285064DB00704B27 /* MainActorWrappers.swift */,
|
||||
);
|
||||
path = Helpers;
|
||||
sourceTree = "<group>";
|
||||
@@ -482,7 +479,6 @@
|
||||
50571E0324393C2600F76F6C /* JustUpdatedChecker.swift in Sources */,
|
||||
5079BA0F250F29BF00EA86F4 /* StoreListView.swift in Sources */,
|
||||
50617DD223FCEFA90099B055 /* PreviewStore.swift in Sources */,
|
||||
50C511B0285064DB00704B27 /* MainActorWrappers.swift in Sources */,
|
||||
5066A6F7251829B1004B5A36 /* ShellConfigurationController.swift in Sources */,
|
||||
50033AC327813F1700253856 /* BundleIDs.swift in Sources */,
|
||||
508A58B3241ED2180069DC07 /* AgentStatusChecker.swift in Sources */,
|
||||
@@ -701,7 +697,7 @@
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_ENTITLEMENTS = Secretive/Secretive.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Developer ID Application";
|
||||
CODE_SIGN_IDENTITY = "";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
@@ -717,7 +713,7 @@
|
||||
MARKETING_VERSION = 1;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.Host;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "Secretive - Host";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Release;
|
||||
@@ -834,10 +830,13 @@
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_ENTITLEMENTS = Secretive/Secretive.entitlements;
|
||||
CODE_SIGN_IDENTITY = "";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"Secretive/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = Z72PRUAWF6;
|
||||
ENABLE_HARDENED_RUNTIME = NO;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
INFOPLIST_FILE = Secretive/Info.plist;
|
||||
@@ -848,6 +847,7 @@
|
||||
MARKETING_VERSION = 1;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.Host;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Test;
|
||||
@@ -878,9 +878,12 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_ENTITLEMENTS = SecretAgent/SecretAgent.entitlements;
|
||||
CODE_SIGN_IDENTITY = "";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"SecretAgent/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = Z72PRUAWF6;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
INFOPLIST_FILE = SecretAgent/Info.plist;
|
||||
@@ -891,6 +894,8 @@
|
||||
MARKETING_VERSION = 1;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.SecretAgent;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Test;
|
||||
@@ -900,6 +905,7 @@
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_ENTITLEMENTS = SecretAgent/SecretAgent.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"SecretAgent/Preview Content\"";
|
||||
@@ -914,6 +920,8 @@
|
||||
MARKETING_VERSION = 1;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.SecretAgent;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Debug;
|
||||
@@ -923,7 +931,7 @@
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_ENTITLEMENTS = SecretAgent/SecretAgent.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Developer ID Application";
|
||||
CODE_SIGN_IDENTITY = "";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"SecretAgent/Preview Content\"";
|
||||
@@ -938,7 +946,8 @@
|
||||
MARKETING_VERSION = 1;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.SecretAgent;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "Secretive - Secret Agent";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Release;
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
import Foundation
|
||||
|
||||
func mainActorWrapped(_ f: @escaping @MainActor () -> Void) -> () -> Void {
|
||||
return {
|
||||
DispatchQueue.main.async {
|
||||
f()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func mainActorWrapped<T: Sendable>(_ f: @escaping @MainActor (T) -> Void) -> (T) -> Void {
|
||||
return { x in
|
||||
DispatchQueue.main.async {
|
||||
f(x)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -43,7 +43,7 @@ struct CreateSecretView<StoreType: SecretStoreModifiable>: View {
|
||||
showing = false
|
||||
}
|
||||
.keyboardShortcut(.cancelAction)
|
||||
Button("Create", action: mainActorWrapped(save))
|
||||
Button("Create", action: save)
|
||||
.disabled(name.isEmpty)
|
||||
.keyboardShortcut(.defaultAction)
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ struct DeleteSecretView<StoreType: SecretStoreModifiable>: View {
|
||||
}
|
||||
HStack {
|
||||
Spacer()
|
||||
Button("Delete", action: mainActorWrapped(delete))
|
||||
Button("Delete", action: delete)
|
||||
.disabled(confirm != secret.name)
|
||||
.keyboardShortcut(.delete)
|
||||
Button("Don't Delete") {
|
||||
|
||||
@@ -28,7 +28,7 @@ struct RenameSecretView<StoreType: SecretStoreModifiable>: View {
|
||||
}
|
||||
HStack {
|
||||
Spacer()
|
||||
Button("Rename", action: mainActorWrapped(rename))
|
||||
Button("Rename", action: rename)
|
||||
.disabled(newName.count == 0)
|
||||
.keyboardShortcut(.return)
|
||||
Button("Cancel") {
|
||||
|
||||
@@ -21,7 +21,7 @@ struct SecretDetailView<SecretType: Secret>: View {
|
||||
CopyableView(title: "Public Key", image: Image(systemName: "key"), text: keyString)
|
||||
Spacer()
|
||||
.frame(height: 20)
|
||||
CopyableView(title: "Public Key Path", image: Image(systemName: "lock.doc"), text: publicKeyFileStoreController.path(for: secret))
|
||||
CopyableView(title: "Public Key Path", image: Image(systemName: "lock.doc"), text: publicKeyFileStoreController.publicKeyPath(for: secret))
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,8 +31,8 @@ struct StoreListView: View {
|
||||
store: store,
|
||||
secret: secret,
|
||||
activeSecret: $activeSecret,
|
||||
deletedSecret: mainActorWrapped(self.secretDeleted),
|
||||
renamedSecret: mainActorWrapped(self.secretRenamed)
|
||||
deletedSecret: self.secretDeleted,
|
||||
renamedSecret: self.secretRenamed
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user