mastodon-ios/AppShared/AppSecret.swift

104 lines
3.0 KiB
Swift

//
// AppSecret.swift
// AppShared
//
// Created by MainasuK Cirno on 2021-4-27.
//
import Foundation
import CryptoKit
import KeychainAccess
import Keys
public final class AppSecret {
public static let keychain = Keychain(service: "org.joinmastodon.app.keychain", accessGroup: AppName.groupID)
static let notificationPrivateKeyName = "notification-private-key-base64"
static let notificationAuthName = "notification-auth-base64"
public let notificationEndpoint: String
public var notificationPrivateKey: P256.KeyAgreement.PrivateKey {
AppSecret.createOrFetchNotificationPrivateKey()
}
public var notificationPublicKey: P256.KeyAgreement.PublicKey {
notificationPrivateKey.publicKey
}
public var notificationAuth: Data {
AppSecret.createOrFetchNotificationAuth()
}
public static let `default`: AppSecret = {
return AppSecret()
}()
init() {
let keys = MastodonKeys()
#if DEBUG
self.notificationEndpoint = keys.notification_endpoint_debug
#else
self.notificationEndpoint = keys.notification_endpoint
#endif
}
public func register() {
_ = AppSecret.createOrFetchNotificationPrivateKey()
_ = AppSecret.createOrFetchNotificationAuth()
}
}
extension AppSecret {
private static func createOrFetchNotificationPrivateKey() -> P256.KeyAgreement.PrivateKey {
if let encoded = AppSecret.keychain[AppSecret.notificationPrivateKeyName],
let data = Data(base64Encoded: encoded) {
do {
let privateKey = try P256.KeyAgreement.PrivateKey(rawRepresentation: data)
return privateKey
} catch {
assertionFailure()
return AppSecret.resetNotificationPrivateKey()
}
} else {
return AppSecret.resetNotificationPrivateKey()
}
}
private static func resetNotificationPrivateKey() -> P256.KeyAgreement.PrivateKey {
let privateKey = P256.KeyAgreement.PrivateKey()
keychain[AppSecret.notificationPrivateKeyName] = privateKey.rawRepresentation.base64EncodedString()
return privateKey
}
}
extension AppSecret {
private static func createOrFetchNotificationAuth() -> Data {
if let encoded = keychain[AppSecret.notificationAuthName],
let data = Data(base64Encoded: encoded) {
return data
} else {
return AppSecret.resetNotificationAuth()
}
}
private static func resetNotificationAuth() -> Data {
let auth = AppSecret.createRandomAuthBytes()
keychain[AppSecret.notificationAuthName] = auth.base64EncodedString()
return auth
}
private static func createRandomAuthBytes() -> Data {
let byteCount = 16
var bytes = Data(count: byteCount)
_ = bytes.withUnsafeMutableBytes { SecRandomCopyBytes(kSecRandomDefault, byteCount, $0.baseAddress!) }
return bytes
}
}