From 7c2b9202b2ee1597f169675d45816cec0af63a81 Mon Sep 17 00:00:00 2001 From: Zorg Date: Fri, 30 Aug 2024 16:30:46 -0700 Subject: [PATCH] Use FileHandle for reading base64 secret key from file Fixes process substitution failing to work for providing the private key as file argument (#2605). --- common_cli/secret.swift | 38 +++++++++++++++++++++++++++++++++++++ generate_appcast/main.swift | 11 +++++++---- generate_keys/main.swift | 6 +++--- sign_update/main.swift | 6 +++--- 4 files changed, 51 insertions(+), 10 deletions(-) diff --git a/common_cli/secret.swift b/common_cli/secret.swift index 1d455ce92..aab9da98e 100644 --- a/common_cli/secret.swift +++ b/common_cli/secret.swift @@ -45,3 +45,41 @@ func decodePrivateAndPublicKeys(secret: Data) -> (privateKey: Data, publicKey: D return (privateKey, publicKey) } + +enum DecodeSecretStringError : LocalizedError { + case unableToReadData + case unableToDecodeDataAsUTF8String + + public var errorDescription: String? { + switch self { + case .unableToReadData: + return "Unable to read EdDSA private key data" + case .unableToDecodeDataAsUTF8String: + return "Unable to read EdDSA private key data as UTF-8 string" + } + } +} + +// Reads secret base64 string from a file +func decodeSecretString(filePath: String) throws -> String { + let privateKeyString: String + if #available(macOS 10.15.4, *) { + // Prefer to use FileHandle which supports process substitution: + // https://github.com/sparkle-project/Sparkle/issues/2605 + let fileHandle = try FileHandle(forReadingFrom: URL(fileURLWithPath: filePath)) + + guard let data = try fileHandle.readToEnd() else { + throw DecodeSecretStringError.unableToReadData + } + + guard let decodedPrivateKeyString = String(data: data, encoding: .utf8) else { + throw DecodeSecretStringError.unableToDecodeDataAsUTF8String + } + + privateKeyString = decodedPrivateKeyString + } else { + privateKeyString = try String(contentsOf: URL(fileURLWithPath: filePath)) + } + + return privateKeyString.trimmingCharacters(in: .whitespacesAndNewlines) +} diff --git a/generate_appcast/main.swift b/generate_appcast/main.swift index d497bf5ae..60476ca75 100644 --- a/generate_appcast/main.swift +++ b/generate_appcast/main.swift @@ -285,19 +285,22 @@ struct GenerateAppcast: ParsableCommand { allowNewPrivateKey = false } else if let privateEdKeyPath = privateEdKeyPath { do { - let privateKeyString: String if privateEdKeyPath == "-" && !FileManager.default.fileExists(atPath: privateEdKeyPath) { if let line = readLine(strippingNewline: true) { - privateKeyString = line + privateEdKeyString = line } else { print("Unable to read EdDSA private key from standard input") throw ExitCode(1) } } else { - privateKeyString = try String(contentsOf: URL(fileURLWithPath: privateEdKeyPath)) + do { + privateEdKeyString = try decodeSecretString(filePath: privateEdKeyPath) + } catch { + print(error.localizedDescription) + throw ExitCode(1) + } } - privateEdKeyString = privateKeyString allowNewPrivateKey = true } catch { print("Unable to load EdDSA private key from", privateEdKeyPath, "\n", error) diff --git a/generate_keys/main.swift b/generate_keys/main.swift index 4b0af7c04..f3dc1d90f 100644 --- a/generate_keys/main.swift +++ b/generate_keys/main.swift @@ -235,12 +235,12 @@ struct GenerateKeys: ParsableCommand { let secretBase64File = importedPrivateKeyFile let secretBase64: String do { - secretBase64 = try String(contentsOfFile: secretBase64File) + secretBase64 = try decodeSecretString(filePath: secretBase64File) } catch { - failure("Failed to read private-key-file: \(error)") + failure("Failed to read private-key-file: \(error.localizedDescription)") } - guard let secret = Data(base64Encoded: secretBase64.trimmingCharacters(in: .whitespacesAndNewlines), options: .init()) else { + guard let secret = Data(base64Encoded: secretBase64, options: .init()) else { failure("Failed to decode base64 encoded key data from: \(secretBase64)") } diff --git a/sign_update/main.swift b/sign_update/main.swift index dbfc8a296..68b3d0c49 100644 --- a/sign_update/main.swift +++ b/sign_update/main.swift @@ -61,13 +61,13 @@ func findKeys(inFile secretFile: String) throws -> (Data, Data) { throw ExitCode(1) } } else { - secretString = try String(contentsOfFile: secretFile) + secretString = try decodeSecretString(filePath: secretFile) } return try findKeys(inString: secretString, allowNewFormat: true) } func findKeys(inString secretBase64String: String, allowNewFormat: Bool) throws -> (Data, Data) { - guard let secret = Data(base64Encoded: secretBase64String.trimmingCharacters(in: .whitespacesAndNewlines), options: .init()) else { + guard let secret = Data(base64Encoded: secretBase64String, options: .init()) else { print("ERROR! Failed to decode base64 encoded key data from: \(secretBase64String)") throw ExitCode.failure } @@ -141,7 +141,7 @@ struct SignUpdate: ParsableCommand { func run() throws { let (priv, pub): (Data, Data) - if let privateKey = privateKey { + if let privateKey = privateKey?.trimmingCharacters(in: .whitespacesAndNewlines) { fputs("Warning: The -s option for passing the private EdDSA key is insecure and deprecated. Please see its help usage for more information.\n", stderr) (priv, pub) = try findKeys(inString: privateKey, allowNewFormat: false)