#!/usr/bin/env swift import Foundation import CryptoKit struct Payload: Codable { let licenseID: String let customerName: String? let customerEmail: String? let edition: String let installationID: String let issuedAt: Date let expiresAt: Date? let maxMajorVersion: Int? } func base64URL(_ data: Data) -> String { data.base64EncodedString() .replacingOccurrences(of: "+", with: "-") .replacingOccurrences(of: "/", with: "_") .replacingOccurrences(of: "=", with: "") } let args = CommandLine.arguments func value(_ flag: String) -> String? { guard let index = args.firstIndex(of: flag), index + 1 < args.count else { return nil } return args[index + 1] } guard let installationID = value("--installation-id"), UUID(uuidString: installationID) != nil else { fputs("Usage: swift Scripts/license-sign.swift --installation-id UUID [--name NAME] [--email EMAIL] [--edition Professional] [--license-id ID] [--max-major 1] [--expires YYYY-MM-DD]\n", stderr) exit(2) } let fm = FileManager.default let defaultPrivate = fm.homeDirectoryForCurrentUser.appendingPathComponent("Library/Application Support/Rsync Studio Licensing/license-private-key.txt") let privateURL = value("--private-key").map { URL(fileURLWithPath: $0) } ?? defaultPrivate let privateText = try String(contentsOf: privateURL, encoding: .utf8).trimmingCharacters(in: .whitespacesAndNewlines) guard let privateData = Data(base64Encoded: privateText) else { throw NSError(domain: "RsyncStudioLicensing", code: 1, userInfo: [NSLocalizedDescriptionKey: "Private key is not Base64."]) } let privateKey = try Curve25519.Signing.PrivateKey(rawRepresentation: privateData) var expiry: Date? if let text = value("--expires") { let formatter = DateFormatter() formatter.locale = Locale(identifier: "en_US_POSIX") formatter.dateFormat = "yyyy-MM-dd" formatter.timeZone = TimeZone(secondsFromGMT: 0) guard let date = formatter.date(from: text) else { fputs("Invalid --expires date; use YYYY-MM-DD.\n", stderr) exit(2) } expiry = date } let payload = Payload( licenseID: value("--license-id") ?? UUID().uuidString.lowercased(), customerName: value("--name"), customerEmail: value("--email"), edition: value("--edition") ?? "Professional", installationID: installationID.lowercased(), issuedAt: Date(), expiresAt: expiry, maxMajorVersion: value("--max-major").flatMap(Int.init) ?? 1 ) let encoder = JSONEncoder() encoder.outputFormatting = [.sortedKeys] encoder.dateEncodingStrategy = .iso8601 let data = try encoder.encode(payload) let signature = try privateKey.signature(for: data) let token = "RS3.\(base64URL(data)).\(base64URL(signature))" print(token)