Compare commits
18 Commits
Author | SHA1 | Date |
---|---|---|
Yoshimasa Niwa | fc21018eb4 | |
mog422 | 52cc08424e | |
Max Goedjen | d13f4ee7ba | |
Max Goedjen | 6f4226f97a | |
Aarni Koskela | 3315a4bfbc | |
Riccardo Pesciarelli | 85a7a64bc9 | |
Max Goedjen | 409efa5f9f | |
Max Goedjen | bb63ae8469 | |
Max Goedjen | 30c1d36974 | |
Max Goedjen | de8d18f9e9 | |
Riccardo Pesciarelli | 1ae0996e2c | |
Moritz Sternemann | 3d50a99430 | |
Marcio Saeger | 45fc356f0f | |
Mahé | 212678b94e | |
RyuS | 2e1f4881a9 | |
Max Goedjen | c2b80e3c7c | |
Mahé | 15d2afd2cb | |
Ale Muñoz | 2a4da36c4e |
Binary file not shown.
Before Width: | Height: | Size: 260 KiB |
|
@ -20,7 +20,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_15.1.app
|
||||
run: sudo xcrun xcode-select -s /Applications/Xcode_15.2.app
|
||||
- name: Update Build Number
|
||||
env:
|
||||
RUN_ID: ${{ github.run_id }}
|
||||
|
|
|
@ -21,7 +21,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_15.1.app
|
||||
run: sudo xcrun xcode-select -s /Applications/Xcode_15.2.app
|
||||
- name: Test
|
||||
run: |
|
||||
pushd Sources/Packages
|
||||
|
@ -43,7 +43,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_15.1.app
|
||||
run: sudo xcrun xcode-select -s /Applications/Xcode_15.2.app
|
||||
- name: Update Build Number
|
||||
env:
|
||||
TAG_NAME: ${{ github.ref }}
|
||||
|
|
|
@ -9,7 +9,7 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set Environment
|
||||
run: sudo xcrun xcode-select -s /Applications/Xcode_15.1.app
|
||||
run: sudo xcrun xcode-select -s /Applications/Xcode_15.2.app
|
||||
- name: Test
|
||||
run: |
|
||||
pushd Sources/Packages
|
||||
|
|
|
@ -16,7 +16,7 @@ Clone Secretive using [these instructions from GitHub](https://docs.github.com/e
|
|||
|
||||
Open [Sources/Secretive.xcodeproj](Sources/Secretive.xcodeproj) in Xcode.
|
||||
|
||||
### Localize the Main App
|
||||
### Translate
|
||||
|
||||
Navigate to [Secretive/Localizable](Sources/Secretive/Localizable.xcstrings).
|
||||
|
||||
|
@ -28,14 +28,6 @@ If your language already has an in-progress localization, select it from the lis
|
|||
|
||||
Start translating! You'll see a list of english phrases, and a space to add a translation of your language.
|
||||
|
||||
### Localize SecretAgent
|
||||
|
||||
Navigate to [Secretive/Localizable](Sources/SecretAgent/Localizable.xcstrings).
|
||||
|
||||
<img src="/.github/readme/localize_sidebar_agent.png" alt="Screenshot of Xcode navigating to the Localizable file" width="300">
|
||||
|
||||
Repeat the same steps from the process of localizing the main app.
|
||||
|
||||
### Create a Pull Request
|
||||
|
||||
Push your changes and open a pull request.
|
||||
|
|
|
@ -17,7 +17,7 @@ extension SecureEnclave {
|
|||
TKTokenWatcher().tokenIDs.contains("com.apple.setoken")
|
||||
}
|
||||
public let id = UUID()
|
||||
public let name = NSLocalizedString("Secure Enclave", comment: "Secure Enclave")
|
||||
public let name = String(localized: "secure_enclave")
|
||||
@Published public private(set) var secrets: [Secret] = []
|
||||
|
||||
private var persistedAuthenticationContexts: [Secret: PersistentAuthenticationContext] = [:]
|
||||
|
@ -107,10 +107,10 @@ extension SecureEnclave {
|
|||
context = existing.context
|
||||
} else {
|
||||
let newContext = LAContext()
|
||||
newContext.localizedCancelTitle = "Deny"
|
||||
newContext.localizedCancelTitle = String(localized: "auth_context_request_deny_button")
|
||||
context = newContext
|
||||
}
|
||||
context.localizedReason = "sign a request from \"\(provenance.origin.displayName)\" using secret \"\(secret.name)\""
|
||||
context.localizedReason = String(localized: "auth_context_request_signature_description_\(provenance.origin.displayName)_\(secret.name)")
|
||||
let attributes = KeychainDictionary([
|
||||
kSecClass: kSecClassKey,
|
||||
kSecAttrKeyClass: kSecAttrKeyClassPrivate,
|
||||
|
@ -140,8 +140,8 @@ extension SecureEnclave {
|
|||
|
||||
public func verify(signature: Data, for data: Data, with secret: Secret) throws -> Bool {
|
||||
let context = LAContext()
|
||||
context.localizedReason = "verify a signature using secret \"\(secret.name)\""
|
||||
context.localizedCancelTitle = "Deny"
|
||||
context.localizedReason = String(localized: "auth_context_request_verify_description_\(secret.name)")
|
||||
context.localizedCancelTitle = String(localized: "auth_context_request_deny_button")
|
||||
let attributes = KeychainDictionary([
|
||||
kSecClass: kSecClassKey,
|
||||
kSecAttrKeyClass: kSecAttrKeyClassPrivate,
|
||||
|
@ -181,16 +181,16 @@ extension SecureEnclave {
|
|||
public func persistAuthentication(secret: Secret, forDuration duration: TimeInterval) throws {
|
||||
let newContext = LAContext()
|
||||
newContext.touchIDAuthenticationAllowableReuseDuration = duration
|
||||
newContext.localizedCancelTitle = "Deny"
|
||||
newContext.localizedCancelTitle = String(localized: "auth_context_request_deny_button")
|
||||
|
||||
let formatter = DateComponentsFormatter()
|
||||
formatter.unitsStyle = .spellOut
|
||||
formatter.allowedUnits = [.hour, .minute, .day]
|
||||
|
||||
if let durationString = formatter.string(from: duration) {
|
||||
newContext.localizedReason = "unlock secret \"\(secret.name)\" for \(durationString)"
|
||||
newContext.localizedReason = String(localized: "auth_context_persist_for_duration_\(secret.name)_\(durationString)")
|
||||
} else {
|
||||
newContext.localizedReason = "unlock secret \"\(secret.name)\""
|
||||
newContext.localizedReason = String(localized: "auth_context_persist_for_duration_unknown_\(secret.name)")
|
||||
}
|
||||
newContext.evaluatePolicy(LAPolicy.deviceOwnerAuthentication, localizedReason: newContext.localizedReason) { [weak self] success, _ in
|
||||
guard success else { return }
|
||||
|
@ -260,7 +260,7 @@ extension SecureEnclave.Store {
|
|||
nil)!
|
||||
|
||||
let wrapped: [SecureEnclave.Secret] = publicTyped.map {
|
||||
let name = $0[kSecAttrLabel] as? String ?? "Unnamed"
|
||||
let name = $0[kSecAttrLabel] as? String ?? String(localized: "unnamed_secret")
|
||||
let id = $0[kSecAttrApplicationLabel] as! Data
|
||||
let publicKeyRef = $0[kSecValueRef] as! SecKey
|
||||
let publicKeyAttributes = SecKeyCopyAttributes(publicKeyRef) as! [CFString: Any]
|
||||
|
|
|
@ -12,7 +12,7 @@ extension SmartCard {
|
|||
|
||||
@Published public var isAvailable: Bool = false
|
||||
public let id = UUID()
|
||||
public private(set) var name = NSLocalizedString("Smart Card", comment: "Smart Card")
|
||||
public private(set) var name = String(localized: "smart_card")
|
||||
@Published public private(set) var secrets: [Secret] = []
|
||||
private let watcher = TKTokenWatcher()
|
||||
private var tokenID: String?
|
||||
|
@ -50,8 +50,8 @@ extension SmartCard {
|
|||
public func sign(data: Data, with secret: Secret, for provenance: SigningRequestProvenance) throws -> Data {
|
||||
guard let tokenID = tokenID else { fatalError() }
|
||||
let context = LAContext()
|
||||
context.localizedReason = "sign a request from \"\(provenance.origin.displayName)\" using secret \"\(secret.name)\""
|
||||
context.localizedCancelTitle = "Deny"
|
||||
context.localizedReason = String(localized: "auth_context_request_signature_description_\(provenance.origin.displayName)_\(secret.name)")
|
||||
context.localizedCancelTitle = String(localized: "auth_context_request_deny_button")
|
||||
let attributes = KeychainDictionary([
|
||||
kSecClass: kSecClassKey,
|
||||
kSecAttrKeyClass: kSecAttrKeyClassPrivate,
|
||||
|
@ -138,7 +138,7 @@ extension SmartCard.Store {
|
|||
private func loadSecrets() {
|
||||
guard let tokenID = tokenID else { return }
|
||||
|
||||
let fallbackName = NSLocalizedString("Smart Card", comment: "Smart Card")
|
||||
let fallbackName = String(localized: "smart_card")
|
||||
if let driverName = watcher.tokenInfo(forTokenID: tokenID)?.driverName {
|
||||
name = driverName
|
||||
} else {
|
||||
|
@ -156,7 +156,7 @@ extension SmartCard.Store {
|
|||
SecItemCopyMatching(attributes, &untyped)
|
||||
guard let typed = untyped as? [[CFString: Any]] else { return }
|
||||
let wrapped = typed.map {
|
||||
let name = $0[kSecAttrLabel] as? String ?? "Unnamed"
|
||||
let name = $0[kSecAttrLabel] as? String ?? String(localized: "unnamed_secret")
|
||||
let tokenID = $0[kSecAttrApplicationLabel] as! Data
|
||||
let algorithm = Algorithm(secAttr: $0[kSecAttrKeyType] as! NSNumber)
|
||||
let keySize = $0[kSecAttrKeySizeInBits] as! Int
|
||||
|
@ -183,8 +183,8 @@ extension SmartCard.Store {
|
|||
/// - Warning: Encryption functions are deliberately only exposed on a library level, and are not exposed in Secretive itself to prevent users from data loss. Any pull requests which expose this functionality in the app will not be merged.
|
||||
public func encrypt(data: Data, with secret: SecretType) throws -> Data {
|
||||
let context = LAContext()
|
||||
context.localizedReason = "encrypt data using secret \"\(secret.name)\""
|
||||
context.localizedCancelTitle = "Deny"
|
||||
context.localizedReason = String(localized: "auth_context_request_encrypt_description_\(secret.name)")
|
||||
context.localizedCancelTitle = String(localized: "auth_context_request_deny_button")
|
||||
let attributes = KeychainDictionary([
|
||||
kSecAttrKeyType: secret.algorithm.secAttrKeyType,
|
||||
kSecAttrKeySizeInBits: secret.keySize,
|
||||
|
@ -212,8 +212,8 @@ extension SmartCard.Store {
|
|||
public func decrypt(data: Data, with secret: SecretType) throws -> Data {
|
||||
guard let tokenID = tokenID else { fatalError() }
|
||||
let context = LAContext()
|
||||
context.localizedReason = "decrypt data using secret \"\(secret.name)\""
|
||||
context.localizedCancelTitle = "Deny"
|
||||
context.localizedReason = String(localized: "auth_context_request_decrypt_description_\(secret.name)")
|
||||
context.localizedCancelTitle = String(localized: "auth_context_request_deny_button")
|
||||
let attributes = KeychainDictionary([
|
||||
kSecClass: kSecClassKey,
|
||||
kSecAttrKeyClass: kSecAttrKeyClassPrivate,
|
||||
|
|
|
@ -1,114 +0,0 @@
|
|||
{
|
||||
"sourceLanguage" : "en",
|
||||
"strings" : {
|
||||
"persist_authentication_accept_button" : {
|
||||
"comment" : "When the user authorizes an action using a secret that requires unlock, they're shown a notification offering to leave the secret unlocked for a set period of time. This is the title for the notification.",
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Leave Unlocked"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"persist_authentication_decline_button" : {
|
||||
"comment" : "When the user authorizes an action using a secret that requires unlock, they're shown a notification offering to leave the secret unlocked for a set period of time. This is the decline button for the notification.",
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Do Not Unlock"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"signed_notification_description_%@" : {
|
||||
"comment" : "When the user performs an action using a secret, they're shown a notification describing what happened. This is the description, showing which secret was used. The placeholder is the name of the secret.",
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Using secret %1$@"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"signed_notification_title_%@" : {
|
||||
"comment" : "When the user performs an action using a secret, they're shown a notification describing what happened. This is the title, showing which app requested the action. The placeholder is the name of the app.",
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Signed Request from %1$@"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"update_notification_ignore_button" : {
|
||||
"comment" : "When an update is available, a notification is shown. This is the button to decline an update.",
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Ignore"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"update_notification_update_button" : {
|
||||
"comment" : "When an update is available, a notification is shown. This is the button to download an update.",
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Update"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"update_notification_update_critical_title_%@" : {
|
||||
"comment" : "When an update is available, a notification is shown. This is the title for a very high priority update with security fixes. The placeholder is for the application version, eg \"Critical Security Update - 2.0\"",
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Critical Security Update - %1$@"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"update_notification_update_description" : {
|
||||
"comment" : "When an update is available, a notification is shown. This is the description to download an update.",
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Click to Update"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"update_notification_update_normal_title_%@" : {
|
||||
"comment" : "When an update is available, a notification is shown. This is the title for a normal priority update. The placeholder is for the application version, eg \"Update Available - 2.0\"",
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Update Available - %1$@"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"version" : "1.0"
|
||||
}
|
|
@ -19,7 +19,6 @@
|
|||
5003EF632780081B00DF2006 /* SecureEnclaveSecretKit in Frameworks */ = {isa = PBXBuildFile; productRef = 5003EF622780081B00DF2006 /* SecureEnclaveSecretKit */; };
|
||||
5003EF652780081B00DF2006 /* SmartCardSecretKit in Frameworks */ = {isa = PBXBuildFile; productRef = 5003EF642780081B00DF2006 /* SmartCardSecretKit */; };
|
||||
500B93C32B478D8400E157DE /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 500B93C22B478D8400E157DE /* Localizable.xcstrings */; };
|
||||
500B93C72B479E2E00E157DE /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 500B93C62B479E2E00E157DE /* Localizable.xcstrings */; };
|
||||
501421622781262300BBAA70 /* Brief in Frameworks */ = {isa = PBXBuildFile; productRef = 501421612781262300BBAA70 /* Brief */; };
|
||||
501421652781268000BBAA70 /* SecretAgent.app in CopyFiles */ = {isa = PBXBuildFile; fileRef = 50A3B78A24026B7500D209EA /* SecretAgent.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
50153E20250AFCB200525160 /* UpdateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50153E1F250AFCB200525160 /* UpdateView.swift */; };
|
||||
|
@ -53,6 +52,7 @@
|
|||
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 */; };
|
||||
50E9CF422B51D596004AB36D /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 500B93C22B478D8400E157DE /* Localizable.xcstrings */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
|
@ -111,7 +111,6 @@
|
|||
50033AC227813F1700253856 /* BundleIDs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BundleIDs.swift; sourceTree = "<group>"; };
|
||||
5003EF39278005C800DF2006 /* Packages */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = Packages; sourceTree = "<group>"; };
|
||||
500B93C22B478D8400E157DE /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Localizable.xcstrings; sourceTree = "<group>"; };
|
||||
500B93C62B479E2E00E157DE /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Localizable.xcstrings; 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>"; };
|
||||
5018F54E24064786002EB505 /* Notifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notifier.swift; sourceTree = "<group>"; };
|
||||
|
@ -316,7 +315,6 @@
|
|||
50A3B79824026B7600D209EA /* Info.plist */,
|
||||
508BF29425B4F140009EFB7E /* InternetAccessPolicy.plist */,
|
||||
50A3B79924026B7600D209EA /* SecretAgent.entitlements */,
|
||||
500B93C62B479E2E00E157DE /* Localizable.xcstrings */,
|
||||
50A3B79224026B7600D209EA /* Preview Content */,
|
||||
);
|
||||
path = SecretAgent;
|
||||
|
@ -431,6 +429,12 @@
|
|||
knownRegions = (
|
||||
en,
|
||||
Base,
|
||||
it,
|
||||
fr,
|
||||
de,
|
||||
"pt-BR",
|
||||
fi,
|
||||
ko,
|
||||
);
|
||||
mainGroup = 50617D7623FCE48D0099B055;
|
||||
productRefGroup = 50617D8023FCE48E0099B055 /* Products */;
|
||||
|
@ -469,7 +473,7 @@
|
|||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
50A3B79724026B7600D209EA /* Main.storyboard in Resources */,
|
||||
500B93C72B479E2E00E157DE /* Localizable.xcstrings in Resources */,
|
||||
50E9CF422B51D596004AB36D /* Localizable.xcstrings in Resources */,
|
||||
50A3B79424026B7600D209EA /* Preview Assets.xcassets in Resources */,
|
||||
50A3B79124026B7600D209EA /* Assets.xcassets in Resources */,
|
||||
508BF2AA25B4F1CB009EFB7E /* InternetAccessPolicy.plist in Resources */,
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -26,7 +26,7 @@ struct SetupView: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
.frame(idealWidth: 500, idealHeight: 500)
|
||||
.frame(minWidth: 500, idealWidth: 500, minHeight: 500, idealHeight: 500)
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -42,16 +42,16 @@ struct UpdateDetailView<UpdaterType: Updater>: View {
|
|||
if let prefix = split.first {
|
||||
switch prefix {
|
||||
case "#":
|
||||
attributed = Text(unprefixed).font(.title) + Text("\n")
|
||||
attributed = Text(unprefixed).font(.title) + Text(verbatim: "\n")
|
||||
case "##":
|
||||
attributed = Text(unprefixed).font(.title2) + Text("\n")
|
||||
attributed = Text(unprefixed).font(.title2) + Text(verbatim: "\n")
|
||||
case "###":
|
||||
attributed = Text(unprefixed).font(.title3) + Text("\n")
|
||||
attributed = Text(unprefixed).font(.title3) + Text(verbatim: "\n")
|
||||
default:
|
||||
attributed = Text(line) + Text("\n\n")
|
||||
attributed = Text(line) + Text(verbatim: "\n\n")
|
||||
}
|
||||
} else {
|
||||
attributed = Text(line) + Text("\n\n")
|
||||
attributed = Text(line) + Text(verbatim: "\n\n")
|
||||
}
|
||||
text = text + attributed
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue