OTPSecureMessenger.swift 1.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758
  1. // LoopFollow
  2. // OTPSecureMessenger.swift
  3. import CryptoKit
  4. import Foundation
  5. struct OTPSecureMessenger {
  6. private let encryptionKey: SymmetricKey
  7. /// Initialize with OTP code. The OTP code is hashed to create the encryption key.
  8. init?(otpCode: String) {
  9. guard let otpData = otpCode.data(using: .utf8) else {
  10. return nil
  11. }
  12. // Use SHA256 hash of OTP code as the encryption key
  13. let hashed = SHA256.hash(data: otpData)
  14. encryptionKey = SymmetricKey(data: hashed)
  15. }
  16. /// Encrypt an encodable object using AES-GCM with OTP-derived key
  17. func encrypt<T: Encodable>(_ object: T) throws -> String {
  18. let dataToEncrypt = try JSONEncoder().encode(object)
  19. // Generate a random nonce (12 bytes for GCM)
  20. let nonce = AES.GCM.Nonce()
  21. // Encrypt using AES-GCM
  22. let sealedBox = try AES.GCM.seal(dataToEncrypt, using: encryptionKey, nonce: nonce)
  23. // Format: nonce (12 bytes) + ciphertext + tag (16 bytes)
  24. let nonceData = Data(nonce)
  25. let ciphertext = sealedBox.ciphertext
  26. let tag = sealedBox.tag
  27. let combinedData = nonceData + ciphertext + tag
  28. return combinedData.base64EncodedString()
  29. }
  30. }
  31. /// Information needed to send a response notification back via APNS
  32. struct ReturnNotificationInfo: Codable {
  33. let productionEnvironment: Bool
  34. let deviceToken: String
  35. let bundleId: String
  36. let teamId: String
  37. let keyId: String
  38. let apnsKey: String
  39. enum CodingKeys: String, CodingKey {
  40. case productionEnvironment = "production_environment"
  41. case deviceToken = "device_token"
  42. case bundleId = "bundle_id"
  43. case teamId = "team_id"
  44. case keyId = "key_id"
  45. case apnsKey = "apns_key"
  46. }
  47. }