diff --git a/PlayTools/MysticRunes/PlayedApple.swift b/PlayTools/MysticRunes/PlayedApple.swift index a1fd3c79..e5c3b373 100644 --- a/PlayTools/MysticRunes/PlayedApple.swift +++ b/PlayTools/MysticRunes/PlayedApple.swift @@ -198,4 +198,95 @@ public class PlayKeychain: NSObject { return errSecItemNotFound } + + @objc static public func keyCreateRandomKey(_ parameters: NSDictionary, + error: UnsafeMutablePointer?>?) + -> Unmanaged? { + // Check if kSecAttrIsPermanent is set to 1 in kSecPrivateKeyAttrs. + // If it is, set it to 0 before fowarding the call to SecKeyCreateRandomKey, + // and then add the key to the keychain db with the original attributes (with kSecAttrIsPermanent set to 1) + var privateKeyAttrs = parameters[kSecPrivateKeyAttrs as String] as? [String: Any] ?? [:] + let isPermanent = privateKeyAttrs[kSecAttrIsPermanent as String] as? Bool ?? false + if isPermanent { + privateKeyAttrs[kSecAttrIsPermanent as String] = false + } + var parametersCopy = parameters as! [String: Any] // swiftlint:disable:this force_cast + parametersCopy[kSecPrivateKeyAttrs as String] = privateKeyAttrs + var error: Unmanaged? + guard let key = SecKeyCreateRandomKey(parametersCopy as CFDictionary, &error) else { + debugLogger("Failed to create random key: \(error!.takeRetainedValue())") + return nil + } + if isPermanent { + // Add the key to the keychain db with the original attributes + var keychainDict = [String: Any]() + keychainDict[kSecClass as String] = kSecClassKey + keychainDict[kSecAttrKeyType as String] = parameters[kSecAttrKeyType as String] + keychainDict[kSecAttrKeyClass as String] = parameters[kSecAttrKeyClass as String] + keychainDict["type"] = parameters[kSecAttrKeyType as String] + keychainDict["kcls"] = parameters[kSecAttrKeyClass as String] + keychainDict["v_Data"] = SecKeyCopyExternalRepresentation(key, nil) as? Data + keychainDict["r_Attributes"] = 1 + guard playChainDB.insert(keychainDict as NSDictionary) != nil else { + debugLogger("Failed to write keychain file") + return nil + } + } + return Unmanaged.passRetained(key) + } + + @objc static public func keyGeneratePair(_ parameters: NSDictionary, + publicKey: UnsafeMutablePointer?>?, + privateKey: UnsafeMutablePointer?>?) -> OSStatus { + // Same as above but we need to disable kSecAttrIsPermanent for both the public and private key. + var privateKeyAttrs = parameters[kSecPrivateKeyAttrs as String] as? [String: Any] ?? [:] + let isPrivatePermanent = privateKeyAttrs[kSecAttrIsPermanent as String] as? Bool ?? false + if isPrivatePermanent { + privateKeyAttrs[kSecAttrIsPermanent as String] = false + } + var publicKeyAttrs = parameters[kSecPublicKeyAttrs as String] as? [String: Any] ?? [:] + let isPublicPermanent = (publicKeyAttrs[kSecAttrIsPermanent as String] as? Bool) ?? false + if isPublicPermanent { + publicKeyAttrs[kSecAttrIsPermanent as String] = false + } + var parametersCopy = parameters as! [String: Any] // swiftlint:disable:this force_cast + parametersCopy[kSecPrivateKeyAttrs as String] = privateKeyAttrs + parametersCopy[kSecPublicKeyAttrs as String] = publicKeyAttrs + var newPublicKey: SecKey? + var newPrivateKey: SecKey? + guard SecKeyGeneratePair(parametersCopy as CFDictionary, &newPublicKey, &newPrivateKey) != 0 else { + debugLogger("Failed to generate key pair.") + return errSecMissingEntitlement + } + if isPrivatePermanent { + // Add the keys to the keychain db with the original attributes + let publicKeyRef = newPublicKey + let privateKeyRef = newPrivateKey + var publicKeyDict = [String: Any]() + publicKeyDict[kSecClass as String] = kSecClassKey + publicKeyDict[kSecAttrKeyType as String] = parameters[kSecAttrKeyType as String] + publicKeyDict[kSecAttrKeyClass as String] = kSecAttrKeyClassPublic + publicKeyDict["type"] = parameters[kSecAttrKeyType as String] + publicKeyDict["kcls"] = kSecAttrKeyClassPublic + publicKeyDict["v_Data"] = SecKeyCopyExternalRepresentation(publicKeyRef!, nil) as? Data + publicKeyDict["r_Attributes"] = 1 + guard playChainDB.insert(publicKeyDict as NSDictionary) != nil else { + debugLogger("Failed to write public key to keychain db") + return errSecMissingEntitlement + } + var privateKeyDict = [String: Any]() + privateKeyDict[kSecClass as String] = kSecClassKey + privateKeyDict[kSecAttrKeyType as String] = parameters[kSecAttrKeyType as String] + privateKeyDict[kSecAttrKeyClass as String] = kSecAttrKeyClassPrivate + privateKeyDict["type"] = parameters[kSecAttrKeyType as String] + privateKeyDict["kcls"] = kSecAttrKeyClassPrivate + privateKeyDict["v_Data"] = SecKeyCopyExternalRepresentation(privateKeyRef!, nil) as? Data + privateKeyDict["r_Attributes"] = 1 + guard playChainDB.insert(privateKeyDict as NSDictionary) != nil else { + debugLogger("Failed to write private key to keychain db") + return errSecMissingEntitlement + } + } + return errSecSuccess + } } diff --git a/PlayTools/PlayLoader.m b/PlayTools/PlayLoader.m index b6da6364..42881cef 100644 --- a/PlayTools/PlayLoader.m +++ b/PlayTools/PlayLoader.m @@ -11,6 +11,9 @@ #import #import #import "NSObject+Swizzle.h" +#import + +@import MachO; // Get device model from playcover .plist // With a null terminator @@ -170,10 +173,46 @@ static OSStatus pt_SecItemDelete(CFDictionaryRef query) { return retval; } +static SecKeyRef pt_SecKeyCreateRandomKey(CFDictionaryRef parameters, CFErrorRef *error) { + SecKeyRef result; + if ([[PlaySettings shared] playChain]) { + result = [PlayKeychain keyCreateRandomKey:(__bridge NSDictionary * _Nonnull)(parameters) error:error]; + } else { + result = SecKeyCreateRandomKey(parameters, (void *)error); + } + + if ([[PlaySettings shared] playChainDebugging]) { + [PlayKeychain debugLogger: [NSString stringWithFormat:@"SecKeyCreateRandomKey: %@", parameters]]; + [PlayKeychain debugLogger: [NSString stringWithFormat:@"SecKeyCreateRandomKey result: %@", result]]; + } + + return result; +} + +// Deprecated, but some apps might still use it. +static OSStatus pt_SecKeyGeneratePair(CFDictionaryRef parameters, SecKeyRef *publicKey, SecKeyRef *privateKey) { + OSStatus retval; + if ([[PlaySettings shared] playChain]) { + retval = [PlayKeychain keyGeneratePair:(__bridge NSDictionary * _Nonnull)(parameters) publicKey:(void *)publicKey privateKey:(void *)privateKey]; + } else { + retval = SecKeyGeneratePair(parameters, (void *)publicKey, (void *)privateKey); + } + + if ([[PlaySettings shared] playChainDebugging]) { + [PlayKeychain debugLogger: [NSString stringWithFormat:@"SecKeyGeneratePair: %@", parameters]]; + [PlayKeychain debugLogger: [NSString stringWithFormat:@"SecKeyGeneratePair public key result: %@", publicKey != NULL ? *publicKey : nil]]; + [PlayKeychain debugLogger: [NSString stringWithFormat:@"SecKeyGeneratePair private key result: %@", privateKey != NULL ? *privateKey : nil]]; + } + + return retval; +} + DYLD_INTERPOSE(pt_SecItemCopyMatching, SecItemCopyMatching) DYLD_INTERPOSE(pt_SecItemAdd, SecItemAdd) DYLD_INTERPOSE(pt_SecItemUpdate, SecItemUpdate) DYLD_INTERPOSE(pt_SecItemDelete, SecItemDelete) +DYLD_INTERPOSE(pt_SecKeyCreateRandomKey, SecKeyCreateRandomKey) +DYLD_INTERPOSE(pt_SecKeyGeneratePair, SecKeyGeneratePair) static uint8_t ue_status = 0; @@ -276,6 +315,7 @@ static int pt_usleep(useconds_t time) { return usleep(time); } + DYLD_INTERPOSE(pt_open, open) DYLD_INTERPOSE(pt_stat, stat) DYLD_INTERPOSE(pt_access, access)