mirror of
https://github.com/maxgoedjen/secretive.git
synced 2026-04-09 18:57:22 +02:00
Compare commits
40 Commits
proxystore
...
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
|
#!/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
|
# Create directories for ASC key
|
||||||
mkdir ~/.private_keys
|
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 * * *"
|
- cron: "0 8 * * *"
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: macos-11.0
|
runs-on: macOS-latest
|
||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
@@ -19,7 +19,7 @@ jobs:
|
|||||||
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
|
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
|
||||||
run: ./.github/scripts/signing.sh
|
run: ./.github/scripts/signing.sh
|
||||||
- name: Set Environment
|
- 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
|
- name: Update Build Number
|
||||||
env:
|
env:
|
||||||
RUN_ID: ${{ github.run_id }}
|
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
|
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
|
- name: Document SHAs
|
||||||
run: |
|
run: |
|
||||||
|
echo "sha-512:"
|
||||||
shasum -a 512 Secretive.zip
|
shasum -a 512 Secretive.zip
|
||||||
shasum -a 512 Archive.zip
|
shasum -a 512 Archive.zip
|
||||||
|
echo "sha-256:"
|
||||||
|
shasum -a 256 Secretive.zip
|
||||||
|
shasum -a 256 Archive.zip
|
||||||
- name: Upload App to Artifacts
|
- name: Upload App to Artifacts
|
||||||
uses: actions/upload-artifact@v1
|
uses: actions/upload-artifact@v1
|
||||||
with:
|
with:
|
||||||
name: Secretive.zip
|
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:
|
tags:
|
||||||
- '*'
|
- '*'
|
||||||
jobs:
|
jobs:
|
||||||
test:
|
# test:
|
||||||
runs-on: macos-11.0
|
# runs-on: macOS-latest
|
||||||
timeout-minutes: 10
|
# timeout-minutes: 10
|
||||||
steps:
|
# steps:
|
||||||
- uses: actions/checkout@v1
|
# - uses: actions/checkout@v3
|
||||||
- name: Setup Signing
|
# - name: Setup Signing
|
||||||
env:
|
# env:
|
||||||
SIGNING_DATA: ${{ secrets.SIGNING_DATA }}
|
# SIGNING_DATA: ${{ secrets.SIGNING_DATA }}
|
||||||
SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }}
|
# SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }}
|
||||||
HOST_PROFILE_DATA: ${{ secrets.HOST_PROFILE_DATA }}
|
# HOST_PROFILE_DATA: ${{ secrets.HOST_PROFILE_DATA }}
|
||||||
AGENT_PROFILE_DATA: ${{ secrets.AGENT_PROFILE_DATA }}
|
# AGENT_PROFILE_DATA: ${{ secrets.AGENT_PROFILE_DATA }}
|
||||||
APPLE_API_KEY_DATA: ${{ secrets.APPLE_API_KEY_DATA }}
|
# APPLE_API_KEY_DATA: ${{ secrets.APPLE_API_KEY_DATA }}
|
||||||
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
|
# APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
|
||||||
run: ./.github/scripts/signing.sh
|
# run: ./.github/scripts/signing.sh
|
||||||
- name: Set Environment
|
# - 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
|
# - name: Test
|
||||||
run: |
|
# run: |
|
||||||
pushd Sources/Packages
|
# pushd Sources/Packages
|
||||||
swift test
|
# swift test
|
||||||
popd
|
# popd
|
||||||
|
|
||||||
build:
|
build:
|
||||||
runs-on: macos-11.0
|
runs-on: macOS-latest
|
||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- name: Setup Signing
|
- name: Setup Signing
|
||||||
env:
|
env:
|
||||||
SIGNING_DATA: ${{ secrets.SIGNING_DATA }}
|
SIGNING_DATA: ${{ secrets.SIGNING_DATA }}
|
||||||
@@ -41,7 +42,7 @@ jobs:
|
|||||||
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
|
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
|
||||||
run: ./.github/scripts/signing.sh
|
run: ./.github/scripts/signing.sh
|
||||||
- name: Set Environment
|
- 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
|
- name: Update Build Number
|
||||||
env:
|
env:
|
||||||
TAG_NAME: ${{ github.ref }}
|
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_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
|
sed -i '' -e "s/GITHUB_BUILD_URL/https:\/\/github.com\/maxgoedjen\/secretive\/actions\/runs\/$RUN_ID/g" Sources/Secretive/Credits.rtf
|
||||||
- name: Build
|
- 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
|
- name: Create ZIPs
|
||||||
run: |
|
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
|
ditto -c -k --sequesterRsrc --keepParent Archive.xcarchive ./Archive.zip
|
||||||
- name: Notarize
|
- name: Notarize
|
||||||
env:
|
env:
|
||||||
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
|
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
|
||||||
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
|
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
|
- name: Document SHAs
|
||||||
run: |
|
run: |
|
||||||
|
echo "sha-512:"
|
||||||
shasum -a 512 Secretive.zip
|
shasum -a 512 Secretive.zip
|
||||||
shasum -a 512 Archive.zip
|
shasum -a 512 Archive.zip
|
||||||
|
echo "sha-256:"
|
||||||
|
shasum -a 256 Secretive.zip
|
||||||
|
shasum -a 256 Archive.zip
|
||||||
- name: Create Release
|
- name: Create Release
|
||||||
id: create_release
|
id: create_release
|
||||||
uses: actions/create-release@v1
|
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]
|
on: [push, pull_request]
|
||||||
jobs:
|
jobs:
|
||||||
test:
|
test:
|
||||||
runs-on: macos-11.0
|
runs-on: macOS-latest
|
||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Set Environment
|
- 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
|
- name: Test
|
||||||
run: |
|
run: |
|
||||||
pushd Sources/Packages
|
pushd Sources/Packages
|
||||||
|
|||||||
@@ -26,6 +26,15 @@ Host *
|
|||||||
IdentityAgent /Users/$YOUR_USERNAME/Library/Containers/com.maxgoedjen.Secretive.SecretAgent/Data/socket.ssh
|
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
|
## Cyberduck
|
||||||
|
|
||||||
Add this to `~/Library/LaunchAgents/com.maxgoedjen.Secretive.SecretAgent.plist`
|
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.
|
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
|
## GitKraken
|
||||||
|
|
||||||
Add this to `~/Library/LaunchAgents/com.maxgoedjen.Secretive.SecretAgent.plist`
|
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.
|
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
|
### 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:
|
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 SecretKit
|
||||||
import AppKit
|
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.
|
/// 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 {
|
public class Agent {
|
||||||
|
|
||||||
@@ -11,6 +30,7 @@ public class Agent {
|
|||||||
private let witness: SigningWitness?
|
private let witness: SigningWitness?
|
||||||
private let writer = OpenSSHKeyWriter()
|
private let writer = OpenSSHKeyWriter()
|
||||||
private let requestTracer = SigningRequestTracer()
|
private let requestTracer = SigningRequestTracer()
|
||||||
|
private let certsPath = (NSHomeDirectory() as NSString).appendingPathComponent("PublicKeys") as String
|
||||||
|
|
||||||
/// Initializes an agent with a store list and a witness.
|
/// Initializes an agent with a store list and a witness.
|
||||||
/// - Parameters:
|
/// - Parameters:
|
||||||
@@ -83,12 +103,22 @@ extension Agent {
|
|||||||
var count = UInt32(secrets.count).bigEndian
|
var count = UInt32(secrets.count).bigEndian
|
||||||
let countData = Data(bytes: &count, count: UInt32.bitWidth/8)
|
let countData = Data(bytes: &count, count: UInt32.bitWidth/8)
|
||||||
var keyData = Data()
|
var keyData = Data()
|
||||||
let writer = OpenSSHKeyWriter()
|
|
||||||
for secret in secrets {
|
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))
|
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))
|
keyData.append(writer.lengthAndData(of: curveData))
|
||||||
|
|
||||||
}
|
}
|
||||||
Logger().debug("Agent enumerated \(secrets.count) identities")
|
Logger().debug("Agent enumerated \(secrets.count) identities")
|
||||||
return countData + keyData
|
return countData + keyData
|
||||||
@@ -101,7 +131,13 @@ extension Agent {
|
|||||||
/// - Returns: An OpenSSH formatted Data payload containing the signed data response.
|
/// - Returns: An OpenSSH formatted Data payload containing the signed data response.
|
||||||
func sign(data: Data, provenance: SigningRequestProvenance) throws -> Data {
|
func sign(data: Data, provenance: SigningRequestProvenance) throws -> Data {
|
||||||
let reader = OpenSSHReader(data: 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 {
|
guard let (store, secret) = secret(matching: hash) else {
|
||||||
Logger().debug("Agent did not have a key matching \(hash as NSData)")
|
Logger().debug("Agent did not have a key matching \(hash as NSData)")
|
||||||
throw AgentError.noMatchingKey
|
throw AgentError.noMatchingKey
|
||||||
@@ -161,6 +197,74 @@ extension Agent {
|
|||||||
|
|
||||||
return signedData
|
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 {
|
func process(from pid: Int32) -> SigningRequestProvenance.Process {
|
||||||
var pidAndNameInfo = self.pidAndNameInfo(from: pid)
|
var pidAndNameInfo = self.pidAndNameInfo(from: pid)
|
||||||
let ppid = pidAndNameInfo.kp_eproc.e_ppid != 0 ? pidAndNameInfo.kp_eproc.e_ppid : nil
|
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))
|
let pathPointer = UnsafeMutablePointer<UInt8>.allocate(capacity: Int(MAXPATHLEN))
|
||||||
_ = proc_pidpath(pid, pathPointer, UInt32(MAXPATHLEN))
|
_ = proc_pidpath(pid, pathPointer, UInt32(MAXPATHLEN))
|
||||||
let path = String(cString: pathPointer)
|
let path = String(cString: pathPointer)
|
||||||
|
|||||||
@@ -15,15 +15,21 @@ public class PublicKeyFileStoreController {
|
|||||||
|
|
||||||
/// Writes out the keys specified to disk.
|
/// Writes out the keys specified to disk.
|
||||||
/// - Parameter secrets: The Secrets to generate keys for.
|
/// - 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 {
|
public func generatePublicKeys(for secrets: [AnySecret], clear: Bool = false) throws {
|
||||||
logger.log("Writing public keys to disk")
|
logger.log("Writing public keys to disk")
|
||||||
if clear {
|
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)
|
try? FileManager.default.createDirectory(at: URL(fileURLWithPath: directory), withIntermediateDirectories: false, attributes: nil)
|
||||||
for secret in secrets {
|
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 }
|
guard let data = keyWriter.openSSHString(secret: secret).data(using: .utf8) else { continue }
|
||||||
FileManager.default.createFile(atPath: path, contents: data, attributes: nil)
|
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.
|
/// - Parameter secret: The Secret to return the path for.
|
||||||
/// - Returns: The path to the Secret's public key.
|
/// - 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.
|
/// - 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: "")
|
let minimalHex = keyWriter.openSSHMD5Fingerprint(secret: secret).replacingOccurrences(of: ":", with: "")
|
||||||
return directory.appending("/").appending("\(minimalHex).pub")
|
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:
|
case Notifier.Constants.persistAuthenticationCategoryIdentitifier:
|
||||||
handlePersistAuthenticationResponse(response: response)
|
handlePersistAuthenticationResponse(response: response)
|
||||||
default:
|
default:
|
||||||
fatalError()
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
completionHandler()
|
completionHandler()
|
||||||
|
|||||||
@@ -697,7 +697,7 @@
|
|||||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CODE_SIGN_ENTITLEMENTS = Secretive/Secretive.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Secretive/Secretive.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "Developer ID Application";
|
CODE_SIGN_IDENTITY = "";
|
||||||
CODE_SIGN_STYLE = Manual;
|
CODE_SIGN_STYLE = Manual;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
@@ -713,7 +713,7 @@
|
|||||||
MARKETING_VERSION = 1;
|
MARKETING_VERSION = 1;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.Host;
|
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.Host;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "Secretive - Host";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
@@ -830,10 +830,13 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
|
CODE_SIGN_ENTITLEMENTS = Secretive/Secretive.entitlements;
|
||||||
|
CODE_SIGN_IDENTITY = "";
|
||||||
CODE_SIGN_STYLE = Manual;
|
CODE_SIGN_STYLE = Manual;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
DEVELOPMENT_ASSET_PATHS = "\"Secretive/Preview Content\"";
|
DEVELOPMENT_ASSET_PATHS = "\"Secretive/Preview Content\"";
|
||||||
|
DEVELOPMENT_TEAM = Z72PRUAWF6;
|
||||||
ENABLE_HARDENED_RUNTIME = NO;
|
ENABLE_HARDENED_RUNTIME = NO;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
INFOPLIST_FILE = Secretive/Info.plist;
|
INFOPLIST_FILE = Secretive/Info.plist;
|
||||||
@@ -844,6 +847,7 @@
|
|||||||
MARKETING_VERSION = 1;
|
MARKETING_VERSION = 1;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.Host;
|
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.Host;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
};
|
};
|
||||||
name = Test;
|
name = Test;
|
||||||
@@ -874,9 +878,12 @@
|
|||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
|
CODE_SIGN_ENTITLEMENTS = SecretAgent/SecretAgent.entitlements;
|
||||||
|
CODE_SIGN_IDENTITY = "";
|
||||||
CODE_SIGN_STYLE = Manual;
|
CODE_SIGN_STYLE = Manual;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
DEVELOPMENT_ASSET_PATHS = "\"SecretAgent/Preview Content\"";
|
DEVELOPMENT_ASSET_PATHS = "\"SecretAgent/Preview Content\"";
|
||||||
|
DEVELOPMENT_TEAM = Z72PRUAWF6;
|
||||||
ENABLE_HARDENED_RUNTIME = YES;
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
INFOPLIST_FILE = SecretAgent/Info.plist;
|
INFOPLIST_FILE = SecretAgent/Info.plist;
|
||||||
@@ -887,6 +894,8 @@
|
|||||||
MARKETING_VERSION = 1;
|
MARKETING_VERSION = 1;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.SecretAgent;
|
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.SecretAgent;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
|
SKIP_INSTALL = YES;
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
};
|
};
|
||||||
name = Test;
|
name = Test;
|
||||||
@@ -896,6 +905,7 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CODE_SIGN_ENTITLEMENTS = SecretAgent/SecretAgent.entitlements;
|
CODE_SIGN_ENTITLEMENTS = SecretAgent/SecretAgent.entitlements;
|
||||||
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
DEVELOPMENT_ASSET_PATHS = "\"SecretAgent/Preview Content\"";
|
DEVELOPMENT_ASSET_PATHS = "\"SecretAgent/Preview Content\"";
|
||||||
@@ -910,6 +920,8 @@
|
|||||||
MARKETING_VERSION = 1;
|
MARKETING_VERSION = 1;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.SecretAgent;
|
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.SecretAgent;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
|
SKIP_INSTALL = YES;
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
};
|
};
|
||||||
name = Debug;
|
name = Debug;
|
||||||
@@ -919,7 +931,7 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CODE_SIGN_ENTITLEMENTS = SecretAgent/SecretAgent.entitlements;
|
CODE_SIGN_ENTITLEMENTS = SecretAgent/SecretAgent.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "Developer ID Application";
|
CODE_SIGN_IDENTITY = "";
|
||||||
CODE_SIGN_STYLE = Manual;
|
CODE_SIGN_STYLE = Manual;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
DEVELOPMENT_ASSET_PATHS = "\"SecretAgent/Preview Content\"";
|
DEVELOPMENT_ASSET_PATHS = "\"SecretAgent/Preview Content\"";
|
||||||
@@ -934,7 +946,8 @@
|
|||||||
MARKETING_VERSION = 1;
|
MARKETING_VERSION = 1;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.SecretAgent;
|
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.SecretAgent;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "Secretive - Secret Agent";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
|
SKIP_INSTALL = YES;
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ struct SecretDetailView<SecretType: Secret>: View {
|
|||||||
CopyableView(title: "Public Key", image: Image(systemName: "key"), text: keyString)
|
CopyableView(title: "Public Key", image: Image(systemName: "key"), text: keyString)
|
||||||
Spacer()
|
Spacer()
|
||||||
.frame(height: 20)
|
.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()
|
Spacer()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user